Char/Misc driver update for 5.16-rc1
Here is the big set of char and misc and other tiny driver subsystem updates for 5.16-rc1. Loads of things in here, all of which have been in linux-next for a while with no reported problems (except for one called out below.) Included are: - habanana labs driver updates, including dma_buf usage, reviewed and acked by the dma_buf maintainers - iio driver update (going through this tree not staging as they really do not belong going through that tree anymore) - counter driver updates - hwmon driver updates that the counter drivers needed, acked by the hwmon maintainer - xillybus driver updates - binder driver updates - extcon driver updates - dma_buf module namespaces added (will cause a build error in arm64 for allmodconfig, but that change is on its way through the drm tree) - lkdtm driver updates - pvpanic driver updates - phy driver updates - virt acrn and nitr_enclaves driver updates - smaller char and misc driver updates Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCYYPX2A8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+ymUUgCbB4EKysgLuXYdjUalZDx+vvZO4k0AniS14O4k F+2dVSZ5WX6wumUzCaA6 =bXQM -----END PGP SIGNATURE----- Merge tag 'char-misc-5.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc Pull char/misc driver updates from Greg KH: "Here is the big set of char and misc and other tiny driver subsystem updates for 5.16-rc1. Loads of things in here, all of which have been in linux-next for a while with no reported problems (except for one called out below.) Included are: - habanana labs driver updates, including dma_buf usage, reviewed and acked by the dma_buf maintainers - iio driver update (going through this tree not staging as they really do not belong going through that tree anymore) - counter driver updates - hwmon driver updates that the counter drivers needed, acked by the hwmon maintainer - xillybus driver updates - binder driver updates - extcon driver updates - dma_buf module namespaces added (will cause a build error in arm64 for allmodconfig, but that change is on its way through the drm tree) - lkdtm driver updates - pvpanic driver updates - phy driver updates - virt acrn and nitr_enclaves driver updates - smaller char and misc driver updates" * tag 'char-misc-5.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (386 commits) comedi: dt9812: fix DMA buffers on stack comedi: ni_usb6501: fix NULL-deref in command paths arm64: errata: Enable TRBE workaround for write to out-of-range address arm64: errata: Enable workaround for TRBE overwrite in FILL mode coresight: trbe: Work around write to out of range coresight: trbe: Make sure we have enough space coresight: trbe: Add a helper to determine the minimum buffer size coresight: trbe: Workaround TRBE errata overwrite in FILL mode coresight: trbe: Add infrastructure for Errata handling coresight: trbe: Allow driver to choose a different alignment coresight: trbe: Decouple buffer base from the hardware base coresight: trbe: Add a helper to pad a given buffer area coresight: trbe: Add a helper to calculate the trace generated coresight: trbe: Defer the probe on offline CPUs coresight: trbe: Fix incorrect access of the sink specific data coresight: etm4x: Add ETM PID for Kryo-5XX coresight: trbe: Prohibit trace before disabling TRBE coresight: trbe: End the AUX handle on truncation coresight: trbe: Do not truncate buffer on IRQ coresight: trbe: Fix handling of spurious interrupts ...
This commit is contained in:
Коммит
5c904c66ed
|
@ -226,6 +226,12 @@ Description: Gets the state dump occurring on a CS timeout or failure.
|
|||
Writing an integer X discards X state dumps, so that the
|
||||
next read would return X+1-st newest state dump.
|
||||
|
||||
What: /sys/kernel/debug/habanalabs/hl<n>/timeout_locked
|
||||
Date: Sep 2021
|
||||
KernelVersion: 5.16
|
||||
Contact: obitton@habana.ai
|
||||
Description: Sets the command submission timeout value in seconds.
|
||||
|
||||
What: /sys/kernel/debug/habanalabs/hl<n>/stop_on_err
|
||||
Date: Mar 2020
|
||||
KernelVersion: 5.6
|
||||
|
|
|
@ -203,6 +203,27 @@ Description:
|
|||
both edges:
|
||||
Any state transition.
|
||||
|
||||
What: /sys/bus/counter/devices/counterX/countY/ceiling_component_id
|
||||
What: /sys/bus/counter/devices/counterX/countY/floor_component_id
|
||||
What: /sys/bus/counter/devices/counterX/countY/count_mode_component_id
|
||||
What: /sys/bus/counter/devices/counterX/countY/direction_component_id
|
||||
What: /sys/bus/counter/devices/counterX/countY/enable_component_id
|
||||
What: /sys/bus/counter/devices/counterX/countY/error_noise_component_id
|
||||
What: /sys/bus/counter/devices/counterX/countY/prescaler_component_id
|
||||
What: /sys/bus/counter/devices/counterX/countY/preset_component_id
|
||||
What: /sys/bus/counter/devices/counterX/countY/preset_enable_component_id
|
||||
What: /sys/bus/counter/devices/counterX/countY/signalZ_action_component_id
|
||||
What: /sys/bus/counter/devices/counterX/signalY/cable_fault_component_id
|
||||
What: /sys/bus/counter/devices/counterX/signalY/cable_fault_enable_component_id
|
||||
What: /sys/bus/counter/devices/counterX/signalY/filter_clock_prescaler_component_id
|
||||
What: /sys/bus/counter/devices/counterX/signalY/index_polarity_component_id
|
||||
What: /sys/bus/counter/devices/counterX/signalY/synchronous_mode_component_id
|
||||
KernelVersion: 5.16
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Read-only attribute that indicates the component ID of the
|
||||
respective extension or Synapse.
|
||||
|
||||
What: /sys/bus/counter/devices/counterX/countY/spike_filter_ns
|
||||
KernelVersion: 5.14
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
|
@ -212,6 +233,14 @@ Description:
|
|||
shorter or equal to configured value are ignored. Value 0 means
|
||||
filter is disabled.
|
||||
|
||||
What: /sys/bus/counter/devices/counterX/events_queue_size
|
||||
KernelVersion: 5.16
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Size of the Counter events queue in number of struct
|
||||
counter_event data structures. The number of elements will be
|
||||
rounded-up to a power of 2.
|
||||
|
||||
What: /sys/bus/counter/devices/counterX/name
|
||||
KernelVersion: 5.2
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
|
@ -286,7 +315,14 @@ What: /sys/bus/counter/devices/counterX/signalY/signal
|
|||
KernelVersion: 5.2
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Signal data of Signal Y represented as a string.
|
||||
Signal level state of Signal Y. The following signal level
|
||||
states are available:
|
||||
|
||||
low:
|
||||
Low level state.
|
||||
|
||||
high:
|
||||
High level state.
|
||||
|
||||
What: /sys/bus/counter/devices/counterX/signalY/synchronous_mode
|
||||
KernelVersion: 5.2
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
What: /sys/bus/fsi/devices/XX.XX.00:06/sbefifoX/timeout
|
||||
KernelVersion: 5.15
|
||||
Contact: eajames@linux.ibm.com
|
||||
Description:
|
||||
Indicates whether or not this SBE device has experienced a
|
||||
timeout; i.e. the SBE did not respond within the time allotted
|
||||
by the driver. A value of 1 indicates that a timeout has
|
||||
ocurred and no transfers have completed since the timeout. A
|
||||
value of 0 indicates that no timeout has ocurred, or if one
|
||||
has, more recent transfers have completed successful.
|
|
@ -429,6 +429,7 @@ 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_concentration_co2_scale
|
||||
KernelVersion: 2.6.35
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
|
@ -1957,3 +1958,44 @@ Description:
|
|||
Specify the percent for light sensor relative to the channel
|
||||
absolute value that a data field should change before an event
|
||||
is generated. Units are a percentage of the prior reading.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/calibration_auto_enable
|
||||
Date: June 2020
|
||||
KernelVersion: 5.8
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Some sensors have the ability to apply auto calibration at
|
||||
runtime. For example, it may be necessary to compensate for
|
||||
contaminant build-up in a measurement chamber or optical
|
||||
element deterioration that would otherwise lead to sensor drift.
|
||||
|
||||
Writing 1 or 0 to this attribute will respectively activate or
|
||||
deactivate this auto calibration function.
|
||||
|
||||
Upon reading, the current status is returned.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/calibration_forced_value
|
||||
Date: June 2020
|
||||
KernelVersion: 5.8
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Some sensors have the ability to apply a manual calibration using
|
||||
a known measurement value, perhaps obtained from an external
|
||||
reference device.
|
||||
|
||||
Writing a value to this function will force such a calibration
|
||||
change. For the scd30 the value should be from the range
|
||||
[400 1 2000].
|
||||
|
||||
Note for the scd30 that a valid value may only be obtained once
|
||||
it is has been written. Until then any read back of this value
|
||||
should be ignored. As for the scd4x an error will be returned
|
||||
immediately if the manual calibration has failed.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/calibration_forced_value_available
|
||||
KernelVersion: 5.15
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Available range for the forced calibration value, expressed as:
|
||||
|
||||
- a range specified as "[min step max]"
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
What: /sys/bus/iio/devices/iio:deviceX/in_concentration_co2_calibration_factory
|
||||
Date: August 2021
|
||||
KernelVersion: 5.16
|
||||
Contact: Jacopo Mondi <jacopo@jmondi.org>
|
||||
Description:
|
||||
Writing '1' triggers a 'Factory' calibration cycle.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_concentration_co2_calibration_background
|
||||
Date: August 2021
|
||||
KernelVersion: 5.16
|
||||
Contact: Jacopo Mondi <jacopo@jmondi.org>
|
||||
Description:
|
||||
Writing '1' triggers a 'Background' calibration cycle.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/error_status_available
|
||||
Date: August 2021
|
||||
KernelVersion: 5.16
|
||||
Contact: Jacopo Mondi <jacopo@jmondi.org>
|
||||
Description:
|
||||
Reading returns the list of possible chip error status.
|
||||
Available options are:
|
||||
- 'error_fatal': Analog front-end initialization error
|
||||
- 'error_i2c': Read/write to non-existing register
|
||||
- 'error_algorithm': Corrupted parameters
|
||||
- 'error_calibration': Calibration has failed
|
||||
- 'error_self_diagnostic': Internal interface failure
|
||||
- 'error_out_of_range': Measured concentration out of scale
|
||||
- 'error_memory': Error during memory operations
|
||||
- 'error_no_measurement': Cleared at first measurement
|
||||
- 'error_low_voltage': Sensor regulated voltage too low
|
||||
- 'error_measurement_timeout': Unable to complete measurement
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/error_status
|
||||
Date: August 2021
|
||||
KernelVersion: 5.16
|
||||
Contact: Jacopo Mondi <jacopo@jmondi.org>
|
||||
Description:
|
||||
Reading returns the current chip error status.
|
|
@ -1,34 +0,0 @@
|
|||
What: /sys/bus/iio/devices/iio:deviceX/calibration_auto_enable
|
||||
Date: June 2020
|
||||
KernelVersion: 5.8
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Contaminants build-up in the measurement chamber or optical
|
||||
elements deterioration leads to sensor drift.
|
||||
|
||||
One can compensate for sensor drift by using automatic self
|
||||
calibration procedure (asc).
|
||||
|
||||
Writing 1 or 0 to this attribute will respectively activate or
|
||||
deactivate asc.
|
||||
|
||||
Upon reading current asc status is returned.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/calibration_forced_value
|
||||
Date: June 2020
|
||||
KernelVersion: 5.8
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Contaminants build-up in the measurement chamber or optical
|
||||
elements deterioration leads to sensor drift.
|
||||
|
||||
One can compensate for sensor drift by using forced
|
||||
recalibration (frc). This is useful in case there's known
|
||||
co2 reference available nearby the sensor.
|
||||
|
||||
Picking value from the range [400 1 2000] and writing it to the
|
||||
sensor will set frc.
|
||||
|
||||
Upon reading current frc value is returned. Note that after
|
||||
power cycling default value (i.e 400) is returned even though
|
||||
internally sensor had recalibrated itself.
|
|
@ -0,0 +1,20 @@
|
|||
What: /sys/bus/iio/devices/iio:deviceX/fault_ovuv
|
||||
KernelVersion: 5.11
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Overvoltage or Undervoltage Input fault. The internal circuitry
|
||||
is protected from excessive voltages applied to the thermocouple
|
||||
cables at FORCE+, FORCE2, RTDIN+ & RTDIN-. This circuitry turn
|
||||
off when the input voltage is negative or greater than VDD.
|
||||
|
||||
Reading returns '1' if input voltage is negative or greater
|
||||
than VDD, otherwise '0'.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_filter_notch_center_frequency
|
||||
KernelVersion: 5.11
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Notch frequency in Hz for a noise rejection filter. Used i.e for
|
||||
line noise rejection.
|
||||
|
||||
Valid notch filter values are 50 Hz and 60 Hz.
|
|
@ -0,0 +1,13 @@
|
|||
What: /sys/bus/platform/devices/occ-hwmon.X/ffdc
|
||||
KernelVersion: 5.15
|
||||
Contact: eajames@linux.ibm.com
|
||||
Description:
|
||||
Contains the First Failure Data Capture from the SBEFIFO
|
||||
hardware, if there is any from a previous transfer. Otherwise,
|
||||
the file is empty. The data is cleared when it's been
|
||||
completely read by a user. As the name suggests, only the data
|
||||
from the first error is saved, until it's cleared upon read. The OCC hwmon driver, running on
|
||||
a Baseboard Management Controller (BMC), communicates with
|
||||
POWER9 and up processors over the Self-Boot Engine (SBE) FIFO.
|
||||
In many error conditions, the SBEFIFO will return error data
|
||||
indicating the type of error and system state, etc.
|
|
@ -1,13 +1,13 @@
|
|||
What: /sys/bus/soundwire/devices/sdw-master-N/revision
|
||||
/sys/bus/soundwire/devices/sdw-master-N/clk_stop_modes
|
||||
/sys/bus/soundwire/devices/sdw-master-N/clk_freq
|
||||
/sys/bus/soundwire/devices/sdw-master-N/clk_gears
|
||||
/sys/bus/soundwire/devices/sdw-master-N/default_col
|
||||
/sys/bus/soundwire/devices/sdw-master-N/default_frame_rate
|
||||
/sys/bus/soundwire/devices/sdw-master-N/default_row
|
||||
/sys/bus/soundwire/devices/sdw-master-N/dynamic_shape
|
||||
/sys/bus/soundwire/devices/sdw-master-N/err_threshold
|
||||
/sys/bus/soundwire/devices/sdw-master-N/max_clk_freq
|
||||
What: /sys/bus/soundwire/devices/sdw-master-<N>/revision
|
||||
/sys/bus/soundwire/devices/sdw-master-<N>/clk_stop_modes
|
||||
/sys/bus/soundwire/devices/sdw-master-<N>/clk_freq
|
||||
/sys/bus/soundwire/devices/sdw-master-<N>/clk_gears
|
||||
/sys/bus/soundwire/devices/sdw-master-<N>/default_col
|
||||
/sys/bus/soundwire/devices/sdw-master-<N>/default_frame_rate
|
||||
/sys/bus/soundwire/devices/sdw-master-<N>/default_row
|
||||
/sys/bus/soundwire/devices/sdw-master-<N>/dynamic_shape
|
||||
/sys/bus/soundwire/devices/sdw-master-<N>/err_threshold
|
||||
/sys/bus/soundwire/devices/sdw-master-<N>/max_clk_freq
|
||||
|
||||
Date: April 2020
|
||||
|
||||
|
|
|
@ -64,37 +64,37 @@ Description: SoundWire Slave Data Port-0 DisCo properties.
|
|||
Data port 0 are used by the bus to configure the Data Port 0.
|
||||
|
||||
|
||||
What: /sys/bus/soundwire/devices/sdw:.../dpN_src/max_word
|
||||
/sys/bus/soundwire/devices/sdw:.../dpN_src/min_word
|
||||
/sys/bus/soundwire/devices/sdw:.../dpN_src/words
|
||||
/sys/bus/soundwire/devices/sdw:.../dpN_src/type
|
||||
/sys/bus/soundwire/devices/sdw:.../dpN_src/max_grouping
|
||||
/sys/bus/soundwire/devices/sdw:.../dpN_src/simple_ch_prep_sm
|
||||
/sys/bus/soundwire/devices/sdw:.../dpN_src/ch_prep_timeout
|
||||
/sys/bus/soundwire/devices/sdw:.../dpN_src/imp_def_interrupts
|
||||
/sys/bus/soundwire/devices/sdw:.../dpN_src/min_ch
|
||||
/sys/bus/soundwire/devices/sdw:.../dpN_src/max_ch
|
||||
/sys/bus/soundwire/devices/sdw:.../dpN_src/channels
|
||||
/sys/bus/soundwire/devices/sdw:.../dpN_src/ch_combinations
|
||||
/sys/bus/soundwire/devices/sdw:.../dpN_src/max_async_buffer
|
||||
/sys/bus/soundwire/devices/sdw:.../dpN_src/block_pack_mode
|
||||
/sys/bus/soundwire/devices/sdw:.../dpN_src/port_encoding
|
||||
What: /sys/bus/soundwire/devices/sdw:.../dp<N>_src/max_word
|
||||
/sys/bus/soundwire/devices/sdw:.../dp<N>_src/min_word
|
||||
/sys/bus/soundwire/devices/sdw:.../dp<N>_src/words
|
||||
/sys/bus/soundwire/devices/sdw:.../dp<N>_src/type
|
||||
/sys/bus/soundwire/devices/sdw:.../dp<N>_src/max_grouping
|
||||
/sys/bus/soundwire/devices/sdw:.../dp<N>_src/simple_ch_prep_sm
|
||||
/sys/bus/soundwire/devices/sdw:.../dp<N>_src/ch_prep_timeout
|
||||
/sys/bus/soundwire/devices/sdw:.../dp<N>_src/imp_def_interrupts
|
||||
/sys/bus/soundwire/devices/sdw:.../dp<N>_src/min_ch
|
||||
/sys/bus/soundwire/devices/sdw:.../dp<N>_src/max_ch
|
||||
/sys/bus/soundwire/devices/sdw:.../dp<N>_src/channels
|
||||
/sys/bus/soundwire/devices/sdw:.../dp<N>_src/ch_combinations
|
||||
/sys/bus/soundwire/devices/sdw:.../dp<N>_src/max_async_buffer
|
||||
/sys/bus/soundwire/devices/sdw:.../dp<N>_src/block_pack_mode
|
||||
/sys/bus/soundwire/devices/sdw:.../dp<N>_src/port_encoding
|
||||
|
||||
/sys/bus/soundwire/devices/sdw:.../dpN_sink/max_word
|
||||
/sys/bus/soundwire/devices/sdw:.../dpN_sink/min_word
|
||||
/sys/bus/soundwire/devices/sdw:.../dpN_sink/words
|
||||
/sys/bus/soundwire/devices/sdw:.../dpN_sink/type
|
||||
/sys/bus/soundwire/devices/sdw:.../dpN_sink/max_grouping
|
||||
/sys/bus/soundwire/devices/sdw:.../dpN_sink/simple_ch_prep_sm
|
||||
/sys/bus/soundwire/devices/sdw:.../dpN_sink/ch_prep_timeout
|
||||
/sys/bus/soundwire/devices/sdw:.../dpN_sink/imp_def_interrupts
|
||||
/sys/bus/soundwire/devices/sdw:.../dpN_sink/min_ch
|
||||
/sys/bus/soundwire/devices/sdw:.../dpN_sink/max_ch
|
||||
/sys/bus/soundwire/devices/sdw:.../dpN_sink/channels
|
||||
/sys/bus/soundwire/devices/sdw:.../dpN_sink/ch_combinations
|
||||
/sys/bus/soundwire/devices/sdw:.../dpN_sink/max_async_buffer
|
||||
/sys/bus/soundwire/devices/sdw:.../dpN_sink/block_pack_mode
|
||||
/sys/bus/soundwire/devices/sdw:.../dpN_sink/port_encoding
|
||||
/sys/bus/soundwire/devices/sdw:.../dp<N>_sink/max_word
|
||||
/sys/bus/soundwire/devices/sdw:.../dp<N>_sink/min_word
|
||||
/sys/bus/soundwire/devices/sdw:.../dp<N>_sink/words
|
||||
/sys/bus/soundwire/devices/sdw:.../dp<N>_sink/type
|
||||
/sys/bus/soundwire/devices/sdw:.../dp<N>_sink/max_grouping
|
||||
/sys/bus/soundwire/devices/sdw:.../dp<N>_sink/simple_ch_prep_sm
|
||||
/sys/bus/soundwire/devices/sdw:.../dp<N>_sink/ch_prep_timeout
|
||||
/sys/bus/soundwire/devices/sdw:.../dp<N>_sink/imp_def_interrupts
|
||||
/sys/bus/soundwire/devices/sdw:.../dp<N>_sink/min_ch
|
||||
/sys/bus/soundwire/devices/sdw:.../dp<N>_sink/max_ch
|
||||
/sys/bus/soundwire/devices/sdw:.../dp<N>_sink/channels
|
||||
/sys/bus/soundwire/devices/sdw:.../dp<N>_sink/ch_combinations
|
||||
/sys/bus/soundwire/devices/sdw:.../dp<N>_sink/max_async_buffer
|
||||
/sys/bus/soundwire/devices/sdw:.../dp<N>_sink/block_pack_mode
|
||||
/sys/bus/soundwire/devices/sdw:.../dp<N>_sink/port_encoding
|
||||
|
||||
Date: May 2020
|
||||
|
||||
|
|
|
@ -127,6 +127,11 @@ its hardware characteristcs.
|
|||
* arm,scatter-gather: boolean. Indicates that the TMC-ETR can safely
|
||||
use the SG mode on this system.
|
||||
|
||||
* arm,max-burst-size: The maximum burst size initiated by TMC on the
|
||||
AXI master interface. The burst size can be in the range [0..15],
|
||||
the setting supports one data transfer per burst up to a maximum of
|
||||
16 data transfers per burst.
|
||||
|
||||
* Optional property for CATU :
|
||||
* interrupts : Exactly one SPI may be listed for reporting the address
|
||||
error
|
||||
|
|
|
@ -11,7 +11,9 @@ maintainers:
|
|||
|
||||
properties:
|
||||
compatible:
|
||||
const: ti,tusb320
|
||||
enum:
|
||||
- ti,tusb320
|
||||
- ti,tusb320l
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/accel/adi,adxl313.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Analog Devices ADXL313 3-Axis Digital Accelerometer
|
||||
|
||||
maintainers:
|
||||
- Lucas Stankus <lucas.p.stankus@gmail.com>
|
||||
|
||||
description: |
|
||||
Analog Devices ADXL313 3-Axis Digital Accelerometer that supports
|
||||
both I2C & SPI interfaces.
|
||||
https://www.analog.com/en/products/adxl313.html
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,adxl313
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
spi-3wire: true
|
||||
|
||||
spi-max-frequency: true
|
||||
|
||||
vs-supply:
|
||||
description: Regulator that supplies power to the accelerometer
|
||||
|
||||
vdd-supply:
|
||||
description: Regulator that supplies the digital interface supply voltage
|
||||
|
||||
interrupts:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
interrupt-names:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
items:
|
||||
enum:
|
||||
- INT1
|
||||
- INT2
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
i2c0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
/* Example for a I2C device node */
|
||||
accelerometer@53 {
|
||||
compatible = "adi,adxl313";
|
||||
reg = <0x53>;
|
||||
interrupt-parent = <&gpio0>;
|
||||
interrupts = <0 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "INT1";
|
||||
};
|
||||
};
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
/* Example for a SPI device node */
|
||||
accelerometer@0 {
|
||||
compatible = "adi,adxl313";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <5000000>;
|
||||
interrupt-parent = <&gpio0>;
|
||||
interrupts = <0 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "INT1";
|
||||
};
|
||||
};
|
|
@ -0,0 +1,88 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/accel/adi,adxl355.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Analog Devices ADXL355 3-Axis, Low noise MEMS Accelerometer
|
||||
|
||||
maintainers:
|
||||
- Puranjay Mohan <puranjay12@gmail.com>
|
||||
|
||||
description: |
|
||||
Analog Devices ADXL355 3-Axis, Low noise MEMS Accelerometer that supports
|
||||
both I2C & SPI interfaces
|
||||
https://www.analog.com/en/products/adxl355.html
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,adxl355
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
minItems: 1
|
||||
maxItems: 3
|
||||
description: |
|
||||
Type for DRDY should be IRQ_TYPE_EDGE_RISING.
|
||||
Three configurable interrupt lines exist.
|
||||
|
||||
interrupt-names:
|
||||
description: Specify which interrupt line is in use.
|
||||
items:
|
||||
enum:
|
||||
- INT1
|
||||
- INT2
|
||||
- DRDY
|
||||
minItems: 1
|
||||
maxItems: 3
|
||||
|
||||
vdd-supply:
|
||||
description: Regulator that provides power to the sensor
|
||||
|
||||
vddio-supply:
|
||||
description: Regulator that provides power to the bus
|
||||
|
||||
spi-max-frequency: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
/* Example for a I2C device node */
|
||||
accelerometer@1d {
|
||||
compatible = "adi,adxl355";
|
||||
reg = <0x1d>;
|
||||
interrupt-parent = <&gpio>;
|
||||
interrupts = <25 IRQ_TYPE_EDGE_RISING>;
|
||||
interrupt-names = "DRDY";
|
||||
};
|
||||
};
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
accelerometer@0 {
|
||||
compatible = "adi,adxl355";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <1000000>;
|
||||
interrupt-parent = <&gpio>;
|
||||
interrupts = <25 IRQ_TYPE_EDGE_RISING>;
|
||||
interrupt-names = "DRDY";
|
||||
};
|
||||
};
|
|
@ -21,6 +21,9 @@ properties:
|
|||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
vdd-supply: true
|
||||
vddio-supply: true
|
||||
|
||||
|
|
|
@ -26,19 +26,43 @@ properties:
|
|||
reg:
|
||||
maxItems: 1
|
||||
|
||||
vrefin-supply:
|
||||
description:
|
||||
Buffered ADC reference voltage supply.
|
||||
|
||||
vref-supply:
|
||||
description:
|
||||
ADC reference voltage supply
|
||||
Unbuffered ADC reference voltage supply.
|
||||
|
||||
adi,internal-ref-microvolt:
|
||||
description: |
|
||||
Internal reference voltage selection in microvolts.
|
||||
|
||||
If no internal reference is specified, the channel will default to the
|
||||
external reference defined by vrefin-supply (or vref-supply).
|
||||
vrefin-supply will take precedence over vref-supply if both are defined.
|
||||
|
||||
If no supplies are defined, the reference selection will default to
|
||||
4096mV internal reference.
|
||||
|
||||
enum: [2500000, 4096000]
|
||||
default: 4096000
|
||||
|
||||
|
||||
spi-max-frequency: true
|
||||
|
||||
"#io-channel-cells":
|
||||
'#io-channel-cells':
|
||||
const: 1
|
||||
|
||||
'#address-cells':
|
||||
const: 1
|
||||
|
||||
'#size-cells':
|
||||
const: 0
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- vref-supply
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
|
@ -49,9 +73,30 @@ examples:
|
|||
#size-cells = <0>;
|
||||
|
||||
adc@0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
compatible = "adi,ad7949";
|
||||
reg = <0>;
|
||||
vref-supply = <&vdd_supply>;
|
||||
};
|
||||
|
||||
adc@1 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
compatible = "adi,ad7949";
|
||||
reg = <1>;
|
||||
vrefin-supply = <&vdd_supply>;
|
||||
};
|
||||
|
||||
adc@2 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
compatible = "adi,ad7949";
|
||||
reg = <2>;
|
||||
adi,internal-ref-microvolt = <4096000>;
|
||||
};
|
||||
};
|
||||
...
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/adc/adi,ad799x.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Analog Devices AD799x analog to digital converters
|
||||
|
||||
maintainers:
|
||||
- Michael Hennerich <Michael.Hennerich@analog.com>
|
||||
|
||||
description: |
|
||||
Support for Analog Devices AD7991, AD7992, AD7993, AD7994, AD7995, AD7997, AD7998,
|
||||
AD7999 and similar analog to digital converters.
|
||||
Specifications on the converters can be found at:
|
||||
AD7991, AD7995, AD7999:
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/AD7991_7995_7999.pdf
|
||||
AD7992:
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/AD7992.pdf
|
||||
AD7993, AD7994:
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/AD7993_7994.pdf
|
||||
AD7997, AD7998:
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/AD7997_7998.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,ad7991
|
||||
- adi,ad7992
|
||||
- adi,ad7993
|
||||
- adi,ad7994
|
||||
- adi,ad7995
|
||||
- adi,ad7997
|
||||
- adi,ad7998
|
||||
- adi,ad7999
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
vcc-supply:
|
||||
description:
|
||||
ADC power supply
|
||||
|
||||
vref-supply:
|
||||
description:
|
||||
ADC reference voltage supply, optional for AD7991, AD7995 and AD7999
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
adc1: adc@28 {
|
||||
reg = <0x28>;
|
||||
compatible = "adi,ad7991";
|
||||
interrupts = <13 2>;
|
||||
interrupt-parent = <&gpio6>;
|
||||
|
||||
vcc-supply = <&vcc_3v3>;
|
||||
vref-supply = <&adc_vref>;
|
||||
};
|
||||
};
|
||||
...
|
|
@ -0,0 +1,100 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/adc/aspeed,ast2600-adc.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: ADC that forms part of an ASPEED server management processor.
|
||||
|
||||
maintainers:
|
||||
- Billy Tsai <billy_tsai@aspeedtech.com>
|
||||
|
||||
description: |
|
||||
• 10-bits resolution for 16 voltage channels.
|
||||
• The device split into two individual engine and each contains 8 voltage
|
||||
channels.
|
||||
• Channel scanning can be non-continuous.
|
||||
• Programmable ADC clock frequency.
|
||||
• Programmable upper and lower threshold for each channels.
|
||||
• Interrupt when larger or less than threshold for each channels.
|
||||
• Support hysteresis for each channels.
|
||||
• Built-in a compensating method.
|
||||
• Built-in a register to trim internal reference voltage.
|
||||
• Internal or External reference voltage.
|
||||
• Support 2 Internal reference voltage 1.2v or 2.5v.
|
||||
• Integrate dividing circuit for battery sensing.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- aspeed,ast2600-adc0
|
||||
- aspeed,ast2600-adc1
|
||||
description:
|
||||
Their trimming data, which is used to calibrate internal reference volage,
|
||||
locates in different address of OTP.
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
description:
|
||||
Input clock used to derive the sample clock. Expected to be the
|
||||
SoC's APB clock.
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
"#io-channel-cells":
|
||||
const: 1
|
||||
|
||||
vref-supply:
|
||||
description:
|
||||
The external regulator supply ADC reference voltage.
|
||||
|
||||
aspeed,int-vref-microvolt:
|
||||
enum: [1200000, 2500000]
|
||||
description:
|
||||
ADC internal reference voltage in microvolts.
|
||||
|
||||
aspeed,battery-sensing:
|
||||
type: boolean
|
||||
description:
|
||||
Inform the driver that last channel will be used to sensor battery.
|
||||
|
||||
aspeed,trim-data-valid:
|
||||
type: boolean
|
||||
description: |
|
||||
The ADC reference voltage can be calibrated to obtain the trimming
|
||||
data which will be stored in otp. This property informs the driver that
|
||||
the data store in the otp is valid.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- resets
|
||||
- "#io-channel-cells"
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/ast2600-clock.h>
|
||||
adc0: adc@1e6e9000 {
|
||||
compatible = "aspeed,ast2600-adc0";
|
||||
reg = <0x1e6e9000 0x100>;
|
||||
clocks = <&syscon ASPEED_CLK_APB2>;
|
||||
resets = <&syscon ASPEED_RESET_ADC>;
|
||||
#io-channel-cells = <1>;
|
||||
aspeed,int-vref-microvolt = <2500000>;
|
||||
};
|
||||
adc1: adc@1e6e9100 {
|
||||
compatible = "aspeed,ast2600-adc1";
|
||||
reg = <0x1e6e9100 0x100>;
|
||||
clocks = <&syscon ASPEED_CLK_APB2>;
|
||||
resets = <&syscon ASPEED_RESET_ADC>;
|
||||
#io-channel-cells = <1>;
|
||||
aspeed,int-vref-microvolt = <2500000>;
|
||||
};
|
||||
...
|
|
@ -15,6 +15,7 @@ properties:
|
|||
enum:
|
||||
- atmel,sama5d2-adc
|
||||
- microchip,sam9x60-adc
|
||||
- microchip,sama7g5-adc
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/adc/nxp,imx8qxp-adc.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: NXP IMX8QXP ADC bindings
|
||||
|
||||
maintainers:
|
||||
- Cai Huoqing <caihuoqing@baidu.com>
|
||||
|
||||
description:
|
||||
Supports the ADC found on the IMX8QXP SoC.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: nxp,imx8qxp-adc
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 2
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: per
|
||||
- const: ipg
|
||||
|
||||
assigned-clocks:
|
||||
maxItems: 1
|
||||
|
||||
assigned-clock-rates:
|
||||
maxItems: 1
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
"#io-channel-cells":
|
||||
const: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
- assigned-clocks
|
||||
- assigned-clock-rates
|
||||
- power-domains
|
||||
- "#io-channel-cells"
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/firmware/imx/rsrc.h>
|
||||
soc {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
adc@5a880000 {
|
||||
compatible = "nxp,imx8qxp-adc";
|
||||
reg = <0x0 0x5a880000 0x0 0x10000>;
|
||||
interrupts = <GIC_SPI 240 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clk IMX_SC_R_ADC_0>,
|
||||
<&clk IMX_SC_R_ADC_0>;
|
||||
clock-names = "per", "ipg";
|
||||
assigned-clocks = <&clk IMX_SC_R_ADC_0>;
|
||||
assigned-clock-rates = <24000000>;
|
||||
power-domains = <&pd IMX_SC_R_ADC_0>;
|
||||
#io-channel-cells = <1>;
|
||||
};
|
||||
};
|
||||
...
|
|
@ -222,6 +222,12 @@ patternProperties:
|
|||
'#io-channel-cells':
|
||||
const: 1
|
||||
|
||||
'#address-cells':
|
||||
const: 1
|
||||
|
||||
'#size-cells':
|
||||
const: 0
|
||||
|
||||
interrupts:
|
||||
description: |
|
||||
IRQ Line for the ADC instance. Valid values are:
|
||||
|
@ -256,6 +262,7 @@ patternProperties:
|
|||
- 20 channels, numbered from 0 to 19 (for in0..in19) on stm32h7 and
|
||||
stm32mp1.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
deprecated: true
|
||||
|
||||
st,adc-diff-channels:
|
||||
description: |
|
||||
|
@ -265,7 +272,9 @@ patternProperties:
|
|||
<vinp vinn>, <vinp vinn>,... vinp and vinn are numbered from 0 to 19.
|
||||
|
||||
Note: At least one of "st,adc-channels" or "st,adc-diff-channels" is
|
||||
required. Both properties can be used together. Some channels can be
|
||||
required if no adc generic channel is defined. These legacy channel
|
||||
properties are exclusive with adc generic channel bindings.
|
||||
Both properties can be used together. Some channels can be
|
||||
used as single-ended and some other ones as differential (mixed). But
|
||||
channels can't be configured both as single-ended and differential.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-matrix
|
||||
|
@ -279,6 +288,7 @@ patternProperties:
|
|||
"vinn" indicates negative input number
|
||||
minimum: 0
|
||||
maximum: 19
|
||||
deprecated: true
|
||||
|
||||
st,min-sample-time-nsecs:
|
||||
description:
|
||||
|
@ -289,6 +299,50 @@ patternProperties:
|
|||
list, to set sample time resp. for all channels, or independently for
|
||||
each channel.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
deprecated: true
|
||||
|
||||
nvmem-cells:
|
||||
items:
|
||||
- description: Phandle to the calibration vrefint data provided by otp
|
||||
|
||||
nvmem-cell-names:
|
||||
items:
|
||||
- const: vrefint
|
||||
|
||||
patternProperties:
|
||||
"^channel@([0-9]|1[0-9])$":
|
||||
type: object
|
||||
$ref: "adc.yaml"
|
||||
description: Represents the external channels which are connected to the ADC.
|
||||
|
||||
properties:
|
||||
reg:
|
||||
items:
|
||||
minimum: 0
|
||||
maximum: 19
|
||||
|
||||
label:
|
||||
description: |
|
||||
Unique name to identify which channel this is.
|
||||
Reserved label names "vddcore", "vrefint" and "vbat"
|
||||
are used to identify internal channels with matching names.
|
||||
|
||||
diff-channels:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
items:
|
||||
minimum: 0
|
||||
maximum: 19
|
||||
|
||||
st,min-sample-time-ns:
|
||||
description: |
|
||||
Minimum sampling time in nanoseconds. Depending on hardware (board)
|
||||
e.g. high/low analog input source impedance, fine tune of ADC
|
||||
sampling time may be recommended.
|
||||
|
||||
required:
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
|
@ -369,12 +423,6 @@ patternProperties:
|
|||
|
||||
additionalProperties: false
|
||||
|
||||
anyOf:
|
||||
- required:
|
||||
- st,adc-channels
|
||||
- required:
|
||||
- st,adc-diff-channels
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
@ -451,4 +499,50 @@ examples:
|
|||
// other adc child node follow...
|
||||
};
|
||||
|
||||
- |
|
||||
// Example 3: with stm32mp157c to setup ADC2 with:
|
||||
// - internal channels 13, 14, 15.
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/clock/stm32mp1-clks.h>
|
||||
adc122: adc@48003000 {
|
||||
compatible = "st,stm32mp1-adc-core";
|
||||
reg = <0x48003000 0x400>;
|
||||
interrupts = <GIC_SPI 18 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 90 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&rcc ADC12>, <&rcc ADC12_K>;
|
||||
clock-names = "bus", "adc";
|
||||
booster-supply = <&booster>;
|
||||
vdd-supply = <&vdd>;
|
||||
vdda-supply = <&vdda>;
|
||||
vref-supply = <&vref>;
|
||||
st,syscfg = <&syscfg>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <1>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
adc@100 {
|
||||
compatible = "st,stm32mp1-adc";
|
||||
#io-channel-cells = <1>;
|
||||
reg = <0x100>;
|
||||
interrupts = <1>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
channel@13 {
|
||||
reg = <13>;
|
||||
label = "vrefint";
|
||||
st,min-sample-time-ns = <9000>;
|
||||
};
|
||||
channel@14 {
|
||||
reg = <14>;
|
||||
label = "vddcore";
|
||||
st,min-sample-time-ns = <9000>;
|
||||
};
|
||||
channel@15 {
|
||||
reg = <15>;
|
||||
label = "vbat";
|
||||
st,min-sample-time-ns = <9000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/chemical/senseair,sunrise.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Senseair Sunrise 006-0-0007 CO2 Sensor
|
||||
|
||||
maintainers:
|
||||
- Jacopo Mondi <jacopo@jmondi.org>
|
||||
|
||||
description: |
|
||||
Senseair Sunrise 006-0-0007 is a NDIR CO2 sensor. It supports I2C or UART buses
|
||||
for communications and control.
|
||||
|
||||
Datasheets:
|
||||
https://rmtplusstoragesenseair.blob.core.windows.net/docs/Dev/publicerat/PSP11704.pdf
|
||||
https://rmtplusstoragesenseair.blob.core.windows.net/docs/Dev/publicerat/PSH11649.pdf
|
||||
https://rmtplusstoragesenseair.blob.core.windows.net/docs/Dev/publicerat/TDE5531.pdf
|
||||
https://rmtplusstoragesenseair.blob.core.windows.net/docs/Market/publicerat/TDE7318.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: senseair,sunrise-006-0-0007
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
ndry-gpios:
|
||||
maxItems: 1
|
||||
description:
|
||||
Phandle to the GPIO line connected to the nDRY pin. Typically active low.
|
||||
|
||||
en-gpios:
|
||||
maxItems: 1
|
||||
description:
|
||||
Phandle to the GPIO line connected to the EN pin. Typically active high.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
co2-sensor@68 {
|
||||
compatible = "senseair,sunrise-006-0-0007";
|
||||
reg = <0x68>;
|
||||
};
|
||||
};
|
|
@ -0,0 +1,46 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/chemical/sensirion,scd4x.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Sensirion SCD4X carbon dioxide sensor
|
||||
|
||||
maintainers:
|
||||
- Roan van Dijk <roan@protonic.nl>
|
||||
|
||||
description: |
|
||||
Air quality sensor capable of measuring co2 concentration, temperature
|
||||
and relative humidity.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- sensirion,scd40
|
||||
- sensirion,scd41
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
vdd-supply: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
co2-sensor@62 {
|
||||
compatible = "sensirion,scd41";
|
||||
reg = <0x62>;
|
||||
};
|
||||
};
|
|
@ -54,7 +54,7 @@ examples:
|
|||
|
||||
ad5766@0 {
|
||||
compatible = "adi,ad5766";
|
||||
output-range-microvolts = <(-5000) 5000>;
|
||||
output-range-microvolts = <(-5000000) 5000000>;
|
||||
reg = <0>;
|
||||
spi-cpol;
|
||||
spi-max-frequency = <1000000>;
|
||||
|
|
|
@ -0,0 +1,131 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/frequency/adi,adrf6780.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: ADRF6780 Microwave Upconverter
|
||||
|
||||
maintainers:
|
||||
- Antoniu Miclaus <antoniu.miclaus@analog.com>
|
||||
|
||||
description: |
|
||||
Wideband, microwave upconverter optimized for point to point microwave
|
||||
radio designs operating in the 5.9 GHz to 23.6 GHz frequency range.
|
||||
|
||||
https://www.analog.com/en/products/adrf6780.html
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,adrf6780
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
spi-max-frequency:
|
||||
maximum: 1000000
|
||||
|
||||
clocks:
|
||||
description:
|
||||
Definition of the external clock.
|
||||
minItems: 1
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: lo_in
|
||||
|
||||
clock-output-names:
|
||||
maxItems: 1
|
||||
|
||||
adi,vga-buff-en:
|
||||
description:
|
||||
RF Variable Gain Amplifier Buffer Enable. Gain is controlled by
|
||||
the voltage on the VATT pin.
|
||||
type: boolean
|
||||
|
||||
adi,lo-buff-en:
|
||||
description:
|
||||
Local Oscillator Amplifier Enable. Disable to put the part in
|
||||
a power down state.
|
||||
type: boolean
|
||||
|
||||
adi,if-mode-en:
|
||||
description:
|
||||
Intermediate Frequency Mode Enable. Either IF Mode or I/Q Mode
|
||||
can be enabled at a time.
|
||||
type: boolean
|
||||
|
||||
adi,iq-mode-en:
|
||||
description:
|
||||
I/Q Mode Enable. Either IF Mode or I/Q Mode can be enabled at a
|
||||
time.
|
||||
type: boolean
|
||||
|
||||
adi,lo-x2-en:
|
||||
description:
|
||||
Double the Local Oscillator output frequency from the Local
|
||||
Oscillator Input Frequency. Either LOx1 or LOx2 can be enabled
|
||||
at a time.
|
||||
type: boolean
|
||||
|
||||
adi,lo-ppf-en:
|
||||
description:
|
||||
Local Oscillator input frequency equal to the Local Oscillator
|
||||
output frequency (LO x1). Either LOx1 or LOx2 can be enabled
|
||||
at a time.
|
||||
type: boolean
|
||||
|
||||
adi,lo-en:
|
||||
description:
|
||||
Enable additional cirtuitry in the LO chain. Disable to put the
|
||||
part in a power down state.
|
||||
type: boolean
|
||||
|
||||
adi,uc-bias-en:
|
||||
description:
|
||||
Enable all bias circuitry thourghout the entire part.
|
||||
Disable to put the part in a power down state.
|
||||
type: boolean
|
||||
|
||||
adi,lo-sideband:
|
||||
description:
|
||||
Switch to the Lower LO Sideband. By default the Upper LO
|
||||
sideband is enabled.
|
||||
type: boolean
|
||||
|
||||
adi,vdet-out-en:
|
||||
description:
|
||||
VDET Output Select Enable. Expose the RF detector output to the
|
||||
VDET external pin.
|
||||
type: boolean
|
||||
|
||||
'#clock-cells':
|
||||
const: 0
|
||||
|
||||
dependencies:
|
||||
adi,lo-x2-en: [ "adi,lo-en" ]
|
||||
adi,lo-ppf-en: [ "adi,lo-en" ]
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
adrf6780@0 {
|
||||
compatible = "adi,adrf6780";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <1000000>;
|
||||
clocks = <&adrf6780_lo>;
|
||||
clock-names = "lo_in";
|
||||
};
|
||||
};
|
||||
...
|
|
@ -0,0 +1,51 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/light/liteon,ltr501.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: LiteON LTR501 I2C Proximity and Light sensor
|
||||
|
||||
maintainers:
|
||||
- Nikita Travkin <nikita@trvn.ru>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- liteon,ltr501
|
||||
- liteon,ltr559
|
||||
- liteon,ltr301
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
vdd-supply: true
|
||||
vddio-supply: true
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
light-sensor@23 {
|
||||
compatible = "liteon,ltr559";
|
||||
reg = <0x23>;
|
||||
vdd-supply = <&pm8916_l17>;
|
||||
vddio-supply = <&pm8916_l6>;
|
||||
|
||||
interrupt-parent = <&msmgpio>;
|
||||
interrupts = <115 IRQ_TYPE_EDGE_FALLING>;
|
||||
};
|
||||
};
|
|
@ -17,11 +17,13 @@ properties:
|
|||
- asahi-kasei,ak8963
|
||||
- asahi-kasei,ak09911
|
||||
- asahi-kasei,ak09912
|
||||
- asahi-kasei,ak09916
|
||||
- enum:
|
||||
- ak8975
|
||||
- ak8963
|
||||
- ak09911
|
||||
- ak09912
|
||||
- ak09916
|
||||
deprecated: true
|
||||
|
||||
reg:
|
||||
|
@ -43,6 +45,11 @@ properties:
|
|||
an optional regulator that needs to be on to provide VDD power to
|
||||
the sensor.
|
||||
|
||||
vid-supply:
|
||||
description: |
|
||||
an optional regulator that needs to be on to provide VID power to
|
||||
the sensor.
|
||||
|
||||
mount-matrix:
|
||||
description: an optional 3x3 mounting rotation matrix.
|
||||
|
||||
|
|
|
@ -35,9 +35,18 @@ properties:
|
|||
mux-control-names: true
|
||||
|
||||
channels:
|
||||
$ref: /schemas/types.yaml#/definitions/string-array
|
||||
$ref: /schemas/types.yaml#/definitions/non-unique-string-array
|
||||
description:
|
||||
List of strings, labeling the mux controller states.
|
||||
List of strings, labeling the mux controller states. An empty
|
||||
string for a state means that the channel is not available.
|
||||
|
||||
settle-time-us:
|
||||
default: 0
|
||||
description:
|
||||
Time required for analog signals to settle after muxing.
|
||||
|
||||
"#io-channel-cells":
|
||||
const: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/temperature/maxim,max31865.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Maxim MAX31865 Resistance Temperature Detector.
|
||||
|
||||
maintainers:
|
||||
- Navin Sankar Velliangiri <navin@linumiz.com>
|
||||
|
||||
description: |
|
||||
https://datasheets.maximintegrated.com/en/ds/MAX31865.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: maxim,max31865
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
maxim,3-wire:
|
||||
description:
|
||||
Identifies the number of wires used by the RTD. Setting this property
|
||||
enables 3-wire RTD connection. Else 2-wire or 4-wire RTD connection.
|
||||
type: boolean
|
||||
|
||||
spi-max-frequency: true
|
||||
spi-cpha: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- spi-cpha
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
temp_sensor@0 {
|
||||
compatible = "maxim,max31865";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <400000>;
|
||||
spi-cpha;
|
||||
maxim,3-wire;
|
||||
};
|
||||
};
|
||||
...
|
|
@ -18,13 +18,21 @@ properties:
|
|||
const: brcm,ns-usb2-phy
|
||||
|
||||
reg:
|
||||
items:
|
||||
- description: iomem address range of DMU (Device Management Unit)
|
||||
anyOf:
|
||||
- maxItems: 1
|
||||
description: PHY control register
|
||||
- maxItems: 1
|
||||
description: iomem address range of DMU (Device Management Unit)
|
||||
deprecated: true
|
||||
|
||||
reg-names:
|
||||
items:
|
||||
- const: dmu
|
||||
|
||||
brcm,syscon-clkset:
|
||||
description: phandle to syscon for clkset register
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: USB PHY reference clock
|
||||
|
@ -39,20 +47,25 @@ properties:
|
|||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- reg-names
|
||||
- clocks
|
||||
- clock-names
|
||||
- "#phy-cells"
|
||||
|
||||
oneOf:
|
||||
- required:
|
||||
- brcm,syscon-clkset
|
||||
- required:
|
||||
- reg-names
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/bcm-nsp.h>
|
||||
phy@1800c000 {
|
||||
phy@1800c164 {
|
||||
compatible = "brcm,ns-usb2-phy";
|
||||
reg = <0x1800c000 0x1000>;
|
||||
reg-names = "dmu";
|
||||
reg = <0x1800c164 0x4>;
|
||||
brcm,syscon-clkset = <&clkset>;
|
||||
clocks = <&genpll BCM_NSP_GENPLL_USB_PHY_REF_CLK>;
|
||||
clock-names = "phy-ref-clk";
|
||||
#phy-cells = <0>;
|
||||
|
|
|
@ -81,6 +81,119 @@ patternProperties:
|
|||
properties:
|
||||
vbus-supply: true
|
||||
|
||||
# It can be necessary to adjust the PHY settings to compensate parasitics, which can be due
|
||||
# to USB connector/receptacle, routing, ESD protection component,... Here is the list of
|
||||
# all optional parameters to tune the interface of the PHY (HS for High-Speed, FS for Full-
|
||||
# Speed, LS for Low-Speed)
|
||||
|
||||
st,current-boost-microamp:
|
||||
description: Current boosting in uA
|
||||
enum: [ 1000, 2000 ]
|
||||
|
||||
st,no-lsfs-fb-cap:
|
||||
description: Disables the LS/FS feedback capacitor
|
||||
type: boolean
|
||||
|
||||
st,decrease-hs-slew-rate:
|
||||
description: Decreases the HS driver slew rate by 10%
|
||||
type: boolean
|
||||
|
||||
st,tune-hs-dc-level:
|
||||
description: |
|
||||
Tunes the HS driver DC level
|
||||
- <0> normal level
|
||||
- <1> increases the level by 5 to 7 mV
|
||||
- <2> increases the level by 10 to 14 mV
|
||||
- <3> decreases the level by 5 to 7 mV
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 0
|
||||
maximum: 3
|
||||
default: 0
|
||||
|
||||
st,enable-fs-rftime-tuning:
|
||||
description: Enables the FS rise/fall tuning option
|
||||
type: boolean
|
||||
|
||||
st,enable-hs-rftime-reduction:
|
||||
description: Enables the HS rise/fall reduction feature
|
||||
type: boolean
|
||||
|
||||
st,trim-hs-current:
|
||||
description: |
|
||||
Controls HS driver current trimming for choke compensation
|
||||
- <0> = 18.87 mA target current / nominal + 0%
|
||||
- <1> = 19.165 mA target current / nominal + 1.56%
|
||||
- <2> = 19.46 mA target current / nominal + 3.12%
|
||||
- <3> = 19.755 mA target current / nominal + 4.68%
|
||||
- <4> = 20.05 mA target current / nominal + 6.24%
|
||||
- <5> = 20.345 mA target current / nominal + 7.8%
|
||||
- <6> = 20.64 mA target current / nominal + 9.36%
|
||||
- <7> = 20.935 mA target current / nominal + 10.92%
|
||||
- <8> = 21.23 mA target current / nominal + 12.48%
|
||||
- <9> = 21.525 mA target current / nominal + 14.04%
|
||||
- <10> = 21.82 mA target current / nominal + 15.6%
|
||||
- <11> = 22.115 mA target current / nominal + 17.16%
|
||||
- <12> = 22.458 mA target current / nominal + 19.01%
|
||||
- <13> = 22.755 mA target current / nominal + 20.58%
|
||||
- <14> = 23.052 mA target current / nominal + 22.16%
|
||||
- <15> = 23.348 mA target current / nominal + 23.73%
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 0
|
||||
maximum: 15
|
||||
default: 0
|
||||
|
||||
st,trim-hs-impedance:
|
||||
description: |
|
||||
Controls HS driver impedance tuning for choke compensation
|
||||
- <0> = no impedance offset
|
||||
- <1> = reduce the impedance by 2 ohms
|
||||
- <2> = reduce the impedance by 4 ohms
|
||||
- <3> = reduce the impedance by 6 ohms
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 0
|
||||
maximum: 3
|
||||
default: 0
|
||||
|
||||
st,tune-squelch-level:
|
||||
description: |
|
||||
Tunes the squelch DC threshold value
|
||||
- <0> = no shift in threshold
|
||||
- <1> = threshold shift by +7 mV
|
||||
- <2> = threshold shift by -5 mV
|
||||
- <3> = threshold shift by +14 mV
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 0
|
||||
maximum: 3
|
||||
default: 0
|
||||
|
||||
st,enable-hs-rx-gain-eq:
|
||||
description: Enables the HS Rx gain equalizer
|
||||
type: boolean
|
||||
|
||||
st,tune-hs-rx-offset:
|
||||
description: |
|
||||
Adjusts the HS Rx offset
|
||||
- <0> = no offset
|
||||
- <1> = offset of +5 mV
|
||||
- <2> = offset of +10 mV
|
||||
- <3> = offset of -5 mV
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 0
|
||||
maximum: 3
|
||||
default: 0
|
||||
|
||||
st,no-hs-ftime-ctrl:
|
||||
description: Disables the HS fall time control of single ended signals during pre-emphasis
|
||||
type: boolean
|
||||
|
||||
st,no-lsfs-sc:
|
||||
description: Disables the short circuit protection in LS/FS driver
|
||||
type: boolean
|
||||
|
||||
st,enable-hs-tx-staggering:
|
||||
description: Enables the basic staggering in HS Tx mode
|
||||
type: boolean
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
|
@ -137,6 +250,14 @@ examples:
|
|||
reg = <0>;
|
||||
phy-supply = <&vdd_usb>;
|
||||
#phy-cells = <0>;
|
||||
st,tune-hs-dc-level = <2>;
|
||||
st,enable-fs-rftime-tuning;
|
||||
st,enable-hs-rftime-reduction;
|
||||
st,trim-hs-current = <15>;
|
||||
st,trim-hs-impedance = <1>;
|
||||
st,tune-squelch-level = <3>;
|
||||
st,tune-hs-rx-offset = <2>;
|
||||
st,no-lsfs-sc;
|
||||
connector {
|
||||
compatible = "usb-a-connector";
|
||||
vbus-supply = <&vbus_sw>;
|
||||
|
@ -147,6 +268,14 @@ examples:
|
|||
reg = <1>;
|
||||
phy-supply = <&vdd_usb>;
|
||||
#phy-cells = <1>;
|
||||
st,tune-hs-dc-level = <2>;
|
||||
st,enable-fs-rftime-tuning;
|
||||
st,enable-hs-rftime-reduction;
|
||||
st,trim-hs-current = <15>;
|
||||
st,trim-hs-impedance = <1>;
|
||||
st,tune-squelch-level = <3>;
|
||||
st,tune-hs-rx-offset = <2>;
|
||||
st,no-lsfs-sc;
|
||||
};
|
||||
};
|
||||
...
|
||||
|
|
|
@ -8,7 +8,7 @@ $schema: "http://devicetree.org/meta-schemas/core.yaml#"
|
|||
title: Qualcomm QMP PHY controller
|
||||
|
||||
maintainers:
|
||||
- Manu Gautam <mgautam@codeaurora.org>
|
||||
- Vinod Koul <vkoul@kernel.org>
|
||||
|
||||
description:
|
||||
QMP phy controller supports physical layer functionality for a number of
|
||||
|
@ -27,6 +27,7 @@ properties:
|
|||
- qcom,msm8998-qmp-pcie-phy
|
||||
- qcom,msm8998-qmp-ufs-phy
|
||||
- qcom,msm8998-qmp-usb3-phy
|
||||
- qcom,qcm2290-qmp-usb3-phy
|
||||
- qcom,sc7180-qmp-usb3-phy
|
||||
- qcom,sc8180x-qmp-pcie-phy
|
||||
- qcom,sc8180x-qmp-ufs-phy
|
||||
|
@ -116,8 +117,6 @@ required:
|
|||
- clock-names
|
||||
- resets
|
||||
- reset-names
|
||||
- vdda-phy-supply
|
||||
- vdda-pll-supply
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
|
@ -150,6 +149,9 @@ allOf:
|
|||
items:
|
||||
- const: phy
|
||||
- const: common
|
||||
required:
|
||||
- vdda-phy-supply
|
||||
- vdda-pll-supply
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
|
@ -176,6 +178,9 @@ allOf:
|
|||
items:
|
||||
- const: phy
|
||||
- const: common
|
||||
required:
|
||||
- vdda-phy-supply
|
||||
- vdda-pll-supply
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
|
@ -204,6 +209,9 @@ allOf:
|
|||
- const: phy
|
||||
- const: common
|
||||
- const: cfg
|
||||
required:
|
||||
- vdda-phy-supply
|
||||
- vdda-pll-supply
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
|
@ -233,6 +241,9 @@ allOf:
|
|||
items:
|
||||
- const: phy
|
||||
- const: common
|
||||
required:
|
||||
- vdda-phy-supply
|
||||
- vdda-pll-supply
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
|
@ -253,6 +264,9 @@ allOf:
|
|||
reset-names:
|
||||
items:
|
||||
- const: ufsphy
|
||||
required:
|
||||
- vdda-phy-supply
|
||||
- vdda-pll-supply
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
|
@ -278,34 +292,16 @@ allOf:
|
|||
reset-names:
|
||||
items:
|
||||
- const: ufsphy
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,ipq8074-qmp-pcie-phy
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
items:
|
||||
- description: pipe clk.
|
||||
clock-names:
|
||||
items:
|
||||
- const: pipe_clk
|
||||
resets:
|
||||
items:
|
||||
- description: reset of phy block.
|
||||
- description: phy common block reset.
|
||||
reset-names:
|
||||
items:
|
||||
- const: phy
|
||||
- const: common
|
||||
required:
|
||||
- vdda-phy-supply
|
||||
- vdda-pll-supply
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,ipq6018-qmp-pcie-phy
|
||||
- qcom,ipq8074-qmp-pcie-phy
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
|
@ -356,6 +352,9 @@ allOf:
|
|||
reset-names:
|
||||
items:
|
||||
- const: phy
|
||||
required:
|
||||
- vdda-phy-supply
|
||||
- vdda-pll-supply
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
|
@ -387,6 +386,9 @@ allOf:
|
|||
items:
|
||||
- const: phy
|
||||
- const: common
|
||||
required:
|
||||
- vdda-phy-supply
|
||||
- vdda-pll-supply
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
|
@ -414,6 +416,38 @@ allOf:
|
|||
items:
|
||||
- const: phy
|
||||
- const: common
|
||||
required:
|
||||
- vdda-phy-supply
|
||||
- vdda-pll-supply
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,qcm2290-qmp-usb3-phy
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
items:
|
||||
- description: Phy config clock.
|
||||
- description: 19.2 MHz ref clk.
|
||||
- description: Phy common block aux clock.
|
||||
clock-names:
|
||||
items:
|
||||
- const: cfg_ahb
|
||||
- const: ref
|
||||
- const: com_aux
|
||||
resets:
|
||||
items:
|
||||
- description: phy_phy reset.
|
||||
- description: reset of phy block.
|
||||
reset-names:
|
||||
items:
|
||||
- const: phy_phy
|
||||
- const: phy
|
||||
required:
|
||||
- vdda-phy-supply
|
||||
- vdda-pll-supply
|
||||
|
||||
examples:
|
||||
- |
|
||||
|
|
|
@ -21,6 +21,7 @@ properties:
|
|||
- qcom,ipq8074-qusb2-phy
|
||||
- qcom,msm8996-qusb2-phy
|
||||
- qcom,msm8998-qusb2-phy
|
||||
- qcom,qcm2290-qusb2-phy
|
||||
- qcom,sdm660-qusb2-phy
|
||||
- qcom,ipq6018-qusb2-phy
|
||||
- qcom,sm4250-qusb2-phy
|
||||
|
@ -50,6 +51,10 @@ properties:
|
|||
- const: ref
|
||||
- const: iface
|
||||
|
||||
vdd-supply:
|
||||
description:
|
||||
Phandle to 0.9V regulator supply to PHY digital circuit.
|
||||
|
||||
vdda-pll-supply:
|
||||
description:
|
||||
Phandle to 1.8V regulator supply to PHY refclk pll block.
|
||||
|
@ -156,6 +161,7 @@ required:
|
|||
- "#phy-cells"
|
||||
- clocks
|
||||
- clock-names
|
||||
- vdd-supply
|
||||
- vdda-pll-supply
|
||||
- vdda-phy-dpdm-supply
|
||||
- resets
|
||||
|
@ -174,6 +180,7 @@ examples:
|
|||
<&gcc GCC_RX1_USB2_CLKREF_CLK>;
|
||||
clock-names = "cfg_ahb", "ref";
|
||||
|
||||
vdd-supply = <&pm8994_l28>;
|
||||
vdda-pll-supply = <&pm8994_l12>;
|
||||
vdda-phy-dpdm-supply = <&pm8994_l24>;
|
||||
|
||||
|
|
|
@ -11,13 +11,10 @@ maintainers:
|
|||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- const: rockchip,rk3288-usb-phy
|
||||
- items:
|
||||
- enum:
|
||||
- rockchip,rk3066a-usb-phy
|
||||
- rockchip,rk3188-usb-phy
|
||||
- const: rockchip,rk3288-usb-phy
|
||||
enum:
|
||||
- rockchip,rk3066a-usb-phy
|
||||
- rockchip,rk3188-usb-phy
|
||||
- rockchip,rk3288-usb-phy
|
||||
|
||||
"#address-cells":
|
||||
const: 1
|
||||
|
|
|
@ -667,6 +667,8 @@ patternProperties:
|
|||
description: Linux-specific binding
|
||||
"^linx,.*":
|
||||
description: Linx Technologies
|
||||
"^liteon,.*":
|
||||
description: LITE-ON Technology Corp.
|
||||
"^litex,.*":
|
||||
description: LiteX SoC builder
|
||||
"^lltc,.*":
|
||||
|
@ -1032,6 +1034,8 @@ patternProperties:
|
|||
description: Shenzhen SEI Robotics Co., Ltd
|
||||
"^semtech,.*":
|
||||
description: Semtech Corporation
|
||||
"^senseair,.*":
|
||||
description: Senseair AB
|
||||
"^sensirion,.*":
|
||||
description: Sensirion AG
|
||||
"^sensortek,.*":
|
||||
|
|
|
@ -287,6 +287,7 @@ IIO
|
|||
devm_iio_device_register()
|
||||
devm_iio_dmaengine_buffer_setup()
|
||||
devm_iio_kfifo_buffer_setup()
|
||||
devm_iio_map_array_register()
|
||||
devm_iio_triggered_buffer_setup()
|
||||
devm_iio_trigger_alloc()
|
||||
devm_iio_trigger_register()
|
||||
|
|
|
@ -223,19 +223,6 @@ whether an input line is differential or single-ended) and instead focus
|
|||
on the core idea of what the data and process represent (e.g. position
|
||||
as interpreted from quadrature encoding data).
|
||||
|
||||
Userspace Interface
|
||||
===================
|
||||
|
||||
Several sysfs attributes are generated by the Generic Counter interface,
|
||||
and reside under the /sys/bus/counter/devices/counterX directory, where
|
||||
counterX refers to the respective counter device. Please see
|
||||
Documentation/ABI/testing/sysfs-bus-counter for detailed
|
||||
information on each Generic Counter interface sysfs attribute.
|
||||
|
||||
Through these sysfs attributes, programs and scripts may interact with
|
||||
the Generic Counter paradigm Counts, Signals, and Synapses of respective
|
||||
counter devices.
|
||||
|
||||
Driver API
|
||||
==========
|
||||
|
||||
|
@ -247,11 +234,14 @@ for defining a counter device.
|
|||
.. kernel-doc:: include/linux/counter.h
|
||||
:internal:
|
||||
|
||||
.. kernel-doc:: drivers/counter/counter.c
|
||||
.. kernel-doc:: drivers/counter/counter-core.c
|
||||
:export:
|
||||
|
||||
Implementation
|
||||
==============
|
||||
.. kernel-doc:: drivers/counter/counter-chrdev.c
|
||||
:export:
|
||||
|
||||
Driver Implementation
|
||||
=====================
|
||||
|
||||
To support a counter device, a driver must first allocate the available
|
||||
Counter Signals via counter_signal structures. These Signals should
|
||||
|
@ -267,25 +257,61 @@ respective counter_count structure. These counter_count structures are
|
|||
set to the counts array member of an allocated counter_device structure
|
||||
before the Counter is registered to the system.
|
||||
|
||||
Driver callbacks should be provided to the counter_device structure via
|
||||
a constant counter_ops structure in order to communicate with the
|
||||
device: to read and write various Signals and Counts, and to set and get
|
||||
the "action mode" and "function mode" for various Synapses and Counts
|
||||
respectively.
|
||||
Driver callbacks must be provided to the counter_device structure in
|
||||
order to communicate with the device: to read and write various Signals
|
||||
and Counts, and to set and get the "action mode" and "function mode" for
|
||||
various Synapses and Counts respectively.
|
||||
|
||||
A defined counter_device structure may be registered to the system by
|
||||
passing it to the counter_register function, and unregistered by passing
|
||||
it to the counter_unregister function. Similarly, the
|
||||
devm_counter_register and devm_counter_unregister functions may be used
|
||||
if device memory-managed registration is desired.
|
||||
devm_counter_register function may be used if device memory-managed
|
||||
registration is desired.
|
||||
|
||||
Extension sysfs attributes can be created for auxiliary functionality
|
||||
and data by passing in defined counter_device_ext, counter_count_ext,
|
||||
and counter_signal_ext structures. In these cases, the
|
||||
counter_device_ext structure is used for global/miscellaneous exposure
|
||||
and configuration of the respective Counter device, while the
|
||||
counter_count_ext and counter_signal_ext structures allow for auxiliary
|
||||
exposure and configuration of a specific Count or Signal respectively.
|
||||
The struct counter_comp structure is used to define counter extensions
|
||||
for Signals, Synapses, and Counts.
|
||||
|
||||
The "type" member specifies the type of high-level data (e.g. BOOL,
|
||||
COUNT_DIRECTION, etc.) handled by this extension. The "``*_read``" and
|
||||
"``*_write``" members can then be set by the counter device driver with
|
||||
callbacks to handle that data using native C data types (i.e. u8, u64,
|
||||
etc.).
|
||||
|
||||
Convenience macros such as ``COUNTER_COMP_COUNT_U64`` are provided for
|
||||
use by driver authors. In particular, driver authors are expected to use
|
||||
the provided macros for standard Counter subsystem attributes in order
|
||||
to maintain a consistent interface for userspace. For example, a counter
|
||||
device driver may define several standard attributes like so::
|
||||
|
||||
struct counter_comp count_ext[] = {
|
||||
COUNTER_COMP_DIRECTION(count_direction_read),
|
||||
COUNTER_COMP_ENABLE(count_enable_read, count_enable_write),
|
||||
COUNTER_COMP_CEILING(count_ceiling_read, count_ceiling_write),
|
||||
};
|
||||
|
||||
This makes it simple to see, add, and modify the attributes that are
|
||||
supported by this driver ("direction", "enable", and "ceiling") and to
|
||||
maintain this code without getting lost in a web of struct braces.
|
||||
|
||||
Callbacks must match the function type expected for the respective
|
||||
component or extension. These function types are defined in the struct
|
||||
counter_comp structure as the "``*_read``" and "``*_write``" union
|
||||
members.
|
||||
|
||||
The corresponding callback prototypes for the extensions mentioned in
|
||||
the previous example above would be::
|
||||
|
||||
int count_direction_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
enum counter_count_direction *direction);
|
||||
int count_enable_read(struct counter_device *counter,
|
||||
struct counter_count *count, u8 *enable);
|
||||
int count_enable_write(struct counter_device *counter,
|
||||
struct counter_count *count, u8 enable);
|
||||
int count_ceiling_read(struct counter_device *counter,
|
||||
struct counter_count *count, u64 *ceiling);
|
||||
int count_ceiling_write(struct counter_device *counter,
|
||||
struct counter_count *count, u64 ceiling);
|
||||
|
||||
Determining the type of extension to create is a matter of scope.
|
||||
|
||||
|
@ -313,52 +339,235 @@ Determining the type of extension to create is a matter of scope.
|
|||
chip overheated via a device extension called "error_overtemp":
|
||||
/sys/bus/counter/devices/counterX/error_overtemp
|
||||
|
||||
Architecture
|
||||
============
|
||||
Subsystem Architecture
|
||||
======================
|
||||
|
||||
When the Generic Counter interface counter module is loaded, the
|
||||
counter_init function is called which registers a bus_type named
|
||||
"counter" to the system. Subsequently, when the module is unloaded, the
|
||||
counter_exit function is called which unregisters the bus_type named
|
||||
"counter" from the system.
|
||||
Counter drivers pass and take data natively (i.e. ``u8``, ``u64``, etc.)
|
||||
and the shared counter module handles the translation between the sysfs
|
||||
interface. This guarantees a standard userspace interface for all
|
||||
counter drivers, and enables a Generic Counter chrdev interface via a
|
||||
generalized device driver ABI.
|
||||
|
||||
Counter devices are registered to the system via the counter_register
|
||||
function, and later removed via the counter_unregister function. The
|
||||
counter_register function establishes a unique ID for the Counter
|
||||
device and creates a respective sysfs directory, where X is the
|
||||
mentioned unique ID:
|
||||
A high-level view of how a count value is passed down from a counter
|
||||
driver is exemplified by the following. The driver callbacks are first
|
||||
registered to the Counter core component for use by the Counter
|
||||
userspace interface components::
|
||||
|
||||
/sys/bus/counter/devices/counterX
|
||||
Driver callbacks registration:
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
+----------------------------+
|
||||
| Counter device driver |
|
||||
+----------------------------+
|
||||
| Processes data from device |
|
||||
+----------------------------+
|
||||
|
|
||||
-------------------
|
||||
/ driver callbacks /
|
||||
-------------------
|
||||
|
|
||||
V
|
||||
+----------------------+
|
||||
| Counter core |
|
||||
+----------------------+
|
||||
| Routes device driver |
|
||||
| callbacks to the |
|
||||
| userspace interfaces |
|
||||
+----------------------+
|
||||
|
|
||||
-------------------
|
||||
/ driver callbacks /
|
||||
-------------------
|
||||
|
|
||||
+---------------+---------------+
|
||||
| |
|
||||
V V
|
||||
+--------------------+ +---------------------+
|
||||
| Counter sysfs | | Counter chrdev |
|
||||
+--------------------+ +---------------------+
|
||||
| Translates to the | | Translates to the |
|
||||
| standard Counter | | standard Counter |
|
||||
| sysfs output | | character device |
|
||||
+--------------------+ +---------------------+
|
||||
|
||||
Sysfs attributes are created within the counterX directory to expose
|
||||
functionality, configurations, and data relating to the Counts, Signals,
|
||||
and Synapses of the Counter device, as well as options and information
|
||||
for the Counter device itself.
|
||||
Thereafter, data can be transferred directly between the Counter device
|
||||
driver and Counter userspace interface::
|
||||
|
||||
Each Signal has a directory created to house its relevant sysfs
|
||||
attributes, where Y is the unique ID of the respective Signal:
|
||||
Count data request:
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
----------------------
|
||||
/ Counter device \
|
||||
+----------------------+
|
||||
| Count register: 0x28 |
|
||||
+----------------------+
|
||||
|
|
||||
-----------------
|
||||
/ raw count data /
|
||||
-----------------
|
||||
|
|
||||
V
|
||||
+----------------------------+
|
||||
| Counter device driver |
|
||||
+----------------------------+
|
||||
| Processes data from device |
|
||||
|----------------------------|
|
||||
| Type: u64 |
|
||||
| Value: 42 |
|
||||
+----------------------------+
|
||||
|
|
||||
----------
|
||||
/ u64 /
|
||||
----------
|
||||
|
|
||||
+---------------+---------------+
|
||||
| |
|
||||
V V
|
||||
+--------------------+ +---------------------+
|
||||
| Counter sysfs | | Counter chrdev |
|
||||
+--------------------+ +---------------------+
|
||||
| Translates to the | | Translates to the |
|
||||
| standard Counter | | standard Counter |
|
||||
| sysfs output | | character device |
|
||||
|--------------------| |---------------------|
|
||||
| Type: const char * | | Type: u64 |
|
||||
| Value: "42" | | Value: 42 |
|
||||
+--------------------+ +---------------------+
|
||||
| |
|
||||
--------------- -----------------------
|
||||
/ const char * / / struct counter_event /
|
||||
--------------- -----------------------
|
||||
| |
|
||||
| V
|
||||
| +-----------+
|
||||
| | read |
|
||||
| +-----------+
|
||||
| \ Count: 42 /
|
||||
| -----------
|
||||
|
|
||||
V
|
||||
+--------------------------------------------------+
|
||||
| `/sys/bus/counter/devices/counterX/countY/count` |
|
||||
+--------------------------------------------------+
|
||||
\ Count: "42" /
|
||||
--------------------------------------------------
|
||||
|
||||
/sys/bus/counter/devices/counterX/signalY
|
||||
There are four primary components involved:
|
||||
|
||||
Similarly, each Count has a directory created to house its relevant
|
||||
sysfs attributes, where Y is the unique ID of the respective Count:
|
||||
Counter device driver
|
||||
---------------------
|
||||
Communicates with the hardware device to read/write data; e.g. counter
|
||||
drivers for quadrature encoders, timers, etc.
|
||||
|
||||
/sys/bus/counter/devices/counterX/countY
|
||||
Counter core
|
||||
------------
|
||||
Registers the counter device driver to the system so that the respective
|
||||
callbacks are called during userspace interaction.
|
||||
|
||||
For a more detailed breakdown of the available Generic Counter interface
|
||||
sysfs attributes, please refer to the
|
||||
Documentation/ABI/testing/sysfs-bus-counter file.
|
||||
Counter sysfs
|
||||
-------------
|
||||
Translates counter data to the standard Counter sysfs interface format
|
||||
and vice versa.
|
||||
|
||||
The Signals and Counts associated with the Counter device are registered
|
||||
to the system as well by the counter_register function. The
|
||||
signal_read/signal_write driver callbacks are associated with their
|
||||
respective Signal attributes, while the count_read/count_write and
|
||||
function_get/function_set driver callbacks are associated with their
|
||||
respective Count attributes; similarly, the same is true for the
|
||||
action_get/action_set driver callbacks and their respective Synapse
|
||||
attributes. If a driver callback is left undefined, then the respective
|
||||
read/write permission is left disabled for the relevant attributes.
|
||||
Please refer to the ``Documentation/ABI/testing/sysfs-bus-counter`` file
|
||||
for a detailed breakdown of the available Generic Counter interface
|
||||
sysfs attributes.
|
||||
|
||||
Similarly, extension sysfs attributes are created for the defined
|
||||
counter_device_ext, counter_count_ext, and counter_signal_ext
|
||||
structures that are passed in.
|
||||
Counter chrdev
|
||||
--------------
|
||||
Translates Counter events to the standard Counter character device; data
|
||||
is transferred via standard character device read calls, while Counter
|
||||
events are configured via ioctl calls.
|
||||
|
||||
Sysfs Interface
|
||||
===============
|
||||
|
||||
Several sysfs attributes are generated by the Generic Counter interface,
|
||||
and reside under the ``/sys/bus/counter/devices/counterX`` directory,
|
||||
where ``X`` is to the respective counter device id. Please see
|
||||
``Documentation/ABI/testing/sysfs-bus-counter`` for detailed information
|
||||
on each Generic Counter interface sysfs attribute.
|
||||
|
||||
Through these sysfs attributes, programs and scripts may interact with
|
||||
the Generic Counter paradigm Counts, Signals, and Synapses of respective
|
||||
counter devices.
|
||||
|
||||
Counter Character Device
|
||||
========================
|
||||
|
||||
Counter character device nodes are created under the ``/dev`` directory
|
||||
as ``counterX``, where ``X`` is the respective counter device id.
|
||||
Defines for the standard Counter data types are exposed via the
|
||||
userspace ``include/uapi/linux/counter.h`` file.
|
||||
|
||||
Counter events
|
||||
--------------
|
||||
Counter device drivers can support Counter events by utilizing the
|
||||
``counter_push_event`` function::
|
||||
|
||||
void counter_push_event(struct counter_device *const counter, const u8 event,
|
||||
const u8 channel);
|
||||
|
||||
The event id is specified by the ``event`` parameter; the event channel
|
||||
id is specified by the ``channel`` parameter. When this function is
|
||||
called, the Counter data associated with the respective event is
|
||||
gathered, and a ``struct counter_event`` is generated for each datum and
|
||||
pushed to userspace.
|
||||
|
||||
Counter events can be configured by users to report various Counter
|
||||
data of interest. This can be conceptualized as a list of Counter
|
||||
component read calls to perform. For example:
|
||||
|
||||
+------------------------+------------------------+
|
||||
| COUNTER_EVENT_OVERFLOW | COUNTER_EVENT_INDEX |
|
||||
+========================+========================+
|
||||
| Channel 0 | Channel 0 |
|
||||
+------------------------+------------------------+
|
||||
| * Count 0 | * Signal 0 |
|
||||
| * Count 1 | * Signal 0 Extension 0 |
|
||||
| * Signal 3 | * Extension 4 |
|
||||
| * Count 4 Extension 2 +------------------------+
|
||||
| * Signal 5 Extension 0 | Channel 1 |
|
||||
| +------------------------+
|
||||
| | * Signal 4 |
|
||||
| | * Signal 4 Extension 0 |
|
||||
| | * Count 7 |
|
||||
+------------------------+------------------------+
|
||||
|
||||
When ``counter_push_event(counter, COUNTER_EVENT_INDEX, 1)`` is called
|
||||
for example, it will go down the list for the ``COUNTER_EVENT_INDEX``
|
||||
event channel 1 and execute the read callbacks for Signal 4, Signal 4
|
||||
Extension 0, and Count 7 -- the data returned for each is pushed to a
|
||||
kfifo as a ``struct counter_event``, which userspace can retrieve via a
|
||||
standard read operation on the respective character device node.
|
||||
|
||||
Userspace
|
||||
---------
|
||||
Userspace applications can configure Counter events via ioctl operations
|
||||
on the Counter character device node. There following ioctl codes are
|
||||
supported and provided by the ``linux/counter.h`` userspace header file:
|
||||
|
||||
* :c:macro:`COUNTER_ADD_WATCH_IOCTL`
|
||||
|
||||
* :c:macro:`COUNTER_ENABLE_EVENTS_IOCTL`
|
||||
|
||||
* :c:macro:`COUNTER_DISABLE_EVENTS_IOCTL`
|
||||
|
||||
To configure events to gather Counter data, users first populate a
|
||||
``struct counter_watch`` with the relevant event id, event channel id,
|
||||
and the information for the desired Counter component from which to
|
||||
read, and then pass it via the ``COUNTER_ADD_WATCH_IOCTL`` ioctl
|
||||
command.
|
||||
|
||||
Note that an event can be watched without gathering Counter data by
|
||||
setting the ``component.type`` member equal to
|
||||
``COUNTER_COMPONENT_NONE``. With this configuration the Counter
|
||||
character device will simply populate the event timestamps for those
|
||||
respective ``struct counter_event`` elements and ignore the component
|
||||
value.
|
||||
|
||||
The ``COUNTER_ADD_WATCH_IOCTL`` command will buffer these Counter
|
||||
watches. When ready, the ``COUNTER_ENABLE_EVENTS_IOCTL`` ioctl command
|
||||
may be used to activate these Counter watches.
|
||||
|
||||
Userspace applications can then execute a ``read`` operation (optionally
|
||||
calling ``poll`` first) on the Counter character device node to retrieve
|
||||
``struct counter_event`` elements with the desired data.
|
||||
|
|
|
@ -88,6 +88,7 @@ Code Seq# Include File Comments
|
|||
<http://infiniband.sourceforge.net/>
|
||||
0x20 all drivers/cdrom/cm206.h
|
||||
0x22 all scsi/sg.h
|
||||
0x3E 00-0F linux/counter.h <mailto:linux-iio@vger.kernel.org>
|
||||
'!' 00-1F uapi/linux/seccomp.h
|
||||
'#' 00-3F IEEE 1394 Subsystem
|
||||
Block for the entire subsystem
|
||||
|
|
|
@ -14,12 +14,15 @@ instances [1].
|
|||
For example, an application that processes sensitive data and runs in a VM,
|
||||
can be separated from other applications running in the same VM. This
|
||||
application then runs in a separate VM than the primary VM, namely an enclave.
|
||||
It runs alongside the VM that spawned it. This setup matches low latency
|
||||
applications needs.
|
||||
|
||||
An enclave runs alongside the VM that spawned it. This setup matches low latency
|
||||
applications needs. The resources that are allocated for the enclave, such as
|
||||
memory and CPUs, are carved out of the primary VM. Each enclave is mapped to a
|
||||
process running in the primary VM, that communicates with the NE driver via an
|
||||
ioctl interface.
|
||||
The current supported architectures for the NE kernel driver, available in the
|
||||
upstream Linux kernel, are x86 and ARM64.
|
||||
|
||||
The resources that are allocated for the enclave, such as memory and CPUs, are
|
||||
carved out of the primary VM. Each enclave is mapped to a process running in the
|
||||
primary VM, that communicates with the NE kernel driver via an ioctl interface.
|
||||
|
||||
In this sense, there are two components:
|
||||
|
||||
|
@ -43,8 +46,8 @@ for the enclave VM. An enclave does not have persistent storage attached.
|
|||
The memory regions carved out of the primary VM and given to an enclave need to
|
||||
be aligned 2 MiB / 1 GiB physically contiguous memory regions (or multiple of
|
||||
this size e.g. 8 MiB). The memory can be allocated e.g. by using hugetlbfs from
|
||||
user space [2][3]. The memory size for an enclave needs to be at least 64 MiB.
|
||||
The enclave memory and CPUs need to be from the same NUMA node.
|
||||
user space [2][3][7]. The memory size for an enclave needs to be at least
|
||||
64 MiB. The enclave memory and CPUs need to be from the same NUMA node.
|
||||
|
||||
An enclave runs on dedicated cores. CPU 0 and its CPU siblings need to remain
|
||||
available for the primary VM. A CPU pool has to be set for NE purposes by an
|
||||
|
@ -61,7 +64,7 @@ device is placed in memory below the typical 4 GiB.
|
|||
The application that runs in the enclave needs to be packaged in an enclave
|
||||
image together with the OS ( e.g. kernel, ramdisk, init ) that will run in the
|
||||
enclave VM. The enclave VM has its own kernel and follows the standard Linux
|
||||
boot protocol [6].
|
||||
boot protocol [6][8].
|
||||
|
||||
The kernel bzImage, the kernel command line, the ramdisk(s) are part of the
|
||||
Enclave Image Format (EIF); plus an EIF header including metadata such as magic
|
||||
|
@ -93,3 +96,5 @@ enclave process can exit.
|
|||
[4] https://www.kernel.org/doc/html/latest/admin-guide/kernel-parameters.html
|
||||
[5] https://man7.org/linux/man-pages/man7/vsock.7.html
|
||||
[6] https://www.kernel.org/doc/html/latest/x86/boot.html
|
||||
[7] https://www.kernel.org/doc/html/latest/arm64/hugetlbpage.html
|
||||
[8] https://www.kernel.org/doc/html/latest/arm64/booting.html
|
||||
|
|
42
MAINTAINERS
42
MAINTAINERS
|
@ -581,6 +581,12 @@ L: platform-driver-x86@vger.kernel.org
|
|||
S: Maintained
|
||||
F: drivers/platform/x86/adv_swbutton.c
|
||||
|
||||
ADXL313 THREE-AXIS DIGITAL ACCELEROMETER DRIVER
|
||||
M: Lucas Stankus <lucas.p.stankus@gmail.com>
|
||||
S: Supported
|
||||
F: Documentation/devicetree/bindings/iio/accel/adi,adxl313.yaml
|
||||
F: drivers/iio/accel/adxl313*
|
||||
|
||||
ADXL34X THREE-AXIS DIGITAL ACCELEROMETER DRIVER (ADXL345/ADXL346)
|
||||
M: Michael Hennerich <michael.hennerich@analog.com>
|
||||
S: Supported
|
||||
|
@ -589,6 +595,16 @@ W: http://ez.analog.com/community/linux-device-drivers
|
|||
F: Documentation/devicetree/bindings/iio/accel/adi,adxl345.yaml
|
||||
F: drivers/input/misc/adxl34x.c
|
||||
|
||||
ADXL355 THREE-AXIS DIGITAL ACCELEROMETER DRIVER
|
||||
M: Puranjay Mohan <puranjay12@gmail.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Supported
|
||||
F: Documentation/devicetree/bindings/iio/accel/adi,adxl355.yaml
|
||||
F: drivers/iio/accel/adxl355.h
|
||||
F: drivers/iio/accel/adxl355_core.c
|
||||
F: drivers/iio/accel/adxl355_i2c.c
|
||||
F: drivers/iio/accel/adxl355_spi.c
|
||||
|
||||
ADXL372 THREE-AXIS DIGITAL ACCELEROMETER DRIVER
|
||||
M: Michael Hennerich <michael.hennerich@analog.com>
|
||||
S: Supported
|
||||
|
@ -4852,7 +4868,8 @@ F: Documentation/ABI/testing/sysfs-bus-counter
|
|||
F: Documentation/driver-api/generic-counter.rst
|
||||
F: drivers/counter/
|
||||
F: include/linux/counter.h
|
||||
F: include/linux/counter_enum.h
|
||||
F: include/uapi/linux/counter.h
|
||||
F: tools/counter/
|
||||
|
||||
CP2615 I2C DRIVER
|
||||
M: Bence Csókás <bence98@sch.bme.hu>
|
||||
|
@ -12326,7 +12343,8 @@ F: arch/arm64/boot/dts/marvell/armada-3720-uDPU.dts
|
|||
|
||||
MHI BUS
|
||||
M: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
|
||||
M: Hemant Kumar <hemantk@codeaurora.org>
|
||||
R: Hemant Kumar <hemantk@codeaurora.org>
|
||||
L: mhi@lists.linux.dev
|
||||
L: linux-arm-msm@vger.kernel.org
|
||||
S: Maintained
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/mani/mhi.git
|
||||
|
@ -13614,6 +13632,13 @@ S: Maintained
|
|||
F: Documentation/devicetree/bindings/display/imx/nxp,imx8mq-dcss.yaml
|
||||
F: drivers/gpu/drm/imx/dcss/
|
||||
|
||||
NXP i.MX 8QXP ADC DRIVER
|
||||
M: Cai Huoqing <caihuoqing@baidu.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Supported
|
||||
F: Documentation/devicetree/bindings/iio/adc/nxp,imx8qxp-adc.yaml
|
||||
F: drivers/iio/adc/imx8qxp-adc.c
|
||||
|
||||
NXP PF8100/PF8121A/PF8200 PMIC REGULATOR DEVICE DRIVER
|
||||
M: Jagan Teki <jagan@amarulasolutions.com>
|
||||
S: Maintained
|
||||
|
@ -17027,6 +17052,13 @@ S: Maintained
|
|||
F: drivers/misc/phantom.c
|
||||
F: include/uapi/linux/phantom.h
|
||||
|
||||
SENSEAIR SUNRISE 006-0-0007
|
||||
M: Jacopo Mondi <jacopo@jmondi.org>
|
||||
S: Maintained
|
||||
F: Documentation/ABI/testing/sysfs-bus-iio-chemical-sunrise-co2
|
||||
F: Documentation/devicetree/bindings/iio/chemical/senseair,sunrise.yaml
|
||||
F: drivers/iio/chemical/sunrise_co2.c
|
||||
|
||||
SENSIRION SCD30 CARBON DIOXIDE SENSOR DRIVER
|
||||
M: Tomasz Duszynski <tomasz.duszynski@octakon.com>
|
||||
S: Maintained
|
||||
|
@ -17036,6 +17068,12 @@ F: drivers/iio/chemical/scd30_core.c
|
|||
F: drivers/iio/chemical/scd30_i2c.c
|
||||
F: drivers/iio/chemical/scd30_serial.c
|
||||
|
||||
SENSIRION SCD4X CARBON DIOXIDE SENSOR DRIVER
|
||||
M: Roan van Dijk <roan@protonic.nl>
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/iio/chemical/sensirion,scd4x.yaml
|
||||
F: drivers/iio/chemical/scd4x.c
|
||||
|
||||
SENSIRION SGP40 GAS SENSOR DRIVER
|
||||
M: Andreas Klinger <ak@it-klinger.de>
|
||||
S: Maintained
|
||||
|
|
|
@ -673,7 +673,6 @@ config ARM64_WORKAROUND_TRBE_OVERWRITE_FILL_MODE
|
|||
config ARM64_ERRATUM_2119858
|
||||
bool "Cortex-A710: 2119858: workaround TRBE overwriting trace data in FILL mode"
|
||||
default y
|
||||
depends on COMPILE_TEST # Until the CoreSight TRBE driver changes are in
|
||||
depends on CORESIGHT_TRBE
|
||||
select ARM64_WORKAROUND_TRBE_OVERWRITE_FILL_MODE
|
||||
help
|
||||
|
@ -692,7 +691,6 @@ config ARM64_ERRATUM_2119858
|
|||
config ARM64_ERRATUM_2139208
|
||||
bool "Neoverse-N2: 2139208: workaround TRBE overwriting trace data in FILL mode"
|
||||
default y
|
||||
depends on COMPILE_TEST # Until the CoreSight TRBE driver changes are in
|
||||
depends on CORESIGHT_TRBE
|
||||
select ARM64_WORKAROUND_TRBE_OVERWRITE_FILL_MODE
|
||||
help
|
||||
|
@ -746,7 +744,6 @@ config ARM64_WORKAROUND_TRBE_WRITE_OUT_OF_RANGE
|
|||
|
||||
config ARM64_ERRATUM_2253138
|
||||
bool "Neoverse-N2: 2253138: workaround TRBE writing to address out-of-range"
|
||||
depends on COMPILE_TEST # Until the CoreSight TRBE driver changes are in
|
||||
depends on CORESIGHT_TRBE
|
||||
default y
|
||||
select ARM64_WORKAROUND_TRBE_WRITE_OUT_OF_RANGE
|
||||
|
@ -765,7 +762,6 @@ config ARM64_ERRATUM_2253138
|
|||
|
||||
config ARM64_ERRATUM_2224489
|
||||
bool "Cortex-A710: 2224489: workaround TRBE writing to address out-of-range"
|
||||
depends on COMPILE_TEST # Until the CoreSight TRBE driver changes are in
|
||||
depends on CORESIGHT_TRBE
|
||||
default y
|
||||
select ARM64_WORKAROUND_TRBE_WRITE_OUT_OF_RANGE
|
||||
|
|
|
@ -1870,7 +1870,7 @@ static void binder_transaction_buffer_release(struct binder_proc *proc,
|
|||
binder_dec_node(buffer->target_node, 1, 0);
|
||||
|
||||
off_start_offset = ALIGN(buffer->data_size, sizeof(void *));
|
||||
off_end_offset = is_failure ? failed_at :
|
||||
off_end_offset = is_failure && failed_at ? failed_at :
|
||||
off_start_offset + buffer->offsets_size;
|
||||
for (buffer_offset = off_start_offset; buffer_offset < off_end_offset;
|
||||
buffer_offset += sizeof(binder_size_t)) {
|
||||
|
@ -1956,9 +1956,8 @@ static void binder_transaction_buffer_release(struct binder_proc *proc,
|
|||
binder_size_t fd_buf_size;
|
||||
binder_size_t num_valid;
|
||||
|
||||
if (proc->tsk != current->group_leader) {
|
||||
if (is_failure) {
|
||||
/*
|
||||
* Nothing to do if running in sender context
|
||||
* The fd fixups have not been applied so no
|
||||
* fds need to be closed.
|
||||
*/
|
||||
|
@ -3176,6 +3175,7 @@ err_invalid_target_handle:
|
|||
* binder_free_buf() - free the specified buffer
|
||||
* @proc: binder proc that owns buffer
|
||||
* @buffer: buffer to be freed
|
||||
* @is_failure: failed to send transaction
|
||||
*
|
||||
* If buffer for an async transaction, enqueue the next async
|
||||
* transaction from the node.
|
||||
|
@ -3185,7 +3185,7 @@ err_invalid_target_handle:
|
|||
static void
|
||||
binder_free_buf(struct binder_proc *proc,
|
||||
struct binder_thread *thread,
|
||||
struct binder_buffer *buffer)
|
||||
struct binder_buffer *buffer, bool is_failure)
|
||||
{
|
||||
binder_inner_proc_lock(proc);
|
||||
if (buffer->transaction) {
|
||||
|
@ -3213,7 +3213,7 @@ binder_free_buf(struct binder_proc *proc,
|
|||
binder_node_inner_unlock(buf_node);
|
||||
}
|
||||
trace_binder_transaction_buffer_release(buffer);
|
||||
binder_transaction_buffer_release(proc, thread, buffer, 0, false);
|
||||
binder_transaction_buffer_release(proc, thread, buffer, 0, is_failure);
|
||||
binder_alloc_free_buf(&proc->alloc, buffer);
|
||||
}
|
||||
|
||||
|
@ -3415,7 +3415,7 @@ static int binder_thread_write(struct binder_proc *proc,
|
|||
proc->pid, thread->pid, (u64)data_ptr,
|
||||
buffer->debug_id,
|
||||
buffer->transaction ? "active" : "finished");
|
||||
binder_free_buf(proc, thread, buffer);
|
||||
binder_free_buf(proc, thread, buffer, false);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -4108,7 +4108,7 @@ retry:
|
|||
buffer->transaction = NULL;
|
||||
binder_cleanup_transaction(t, "fd fixups failed",
|
||||
BR_FAILED_REPLY);
|
||||
binder_free_buf(proc, thread, buffer);
|
||||
binder_free_buf(proc, thread, buffer, true);
|
||||
binder_debug(BINDER_DEBUG_FAILED_TRANSACTION,
|
||||
"%d:%d %stransaction %d fd fixups failed %d/%d, line %d\n",
|
||||
proc->pid, thread->pid,
|
||||
|
|
|
@ -495,6 +495,10 @@ static ssize_t read_iter_zero(struct kiocb *iocb, struct iov_iter *iter)
|
|||
written += n;
|
||||
if (signal_pending(current))
|
||||
return written ? written : -ERESTARTSYS;
|
||||
if (!need_resched())
|
||||
continue;
|
||||
if (iocb->ki_flags & IOCB_NOWAIT)
|
||||
return written ? written : -EAGAIN;
|
||||
cond_resched();
|
||||
}
|
||||
return written;
|
||||
|
@ -696,11 +700,11 @@ static const struct memdev {
|
|||
#ifdef CONFIG_DEVMEM
|
||||
[DEVMEM_MINOR] = { "mem", 0, &mem_fops, FMODE_UNSIGNED_OFFSET },
|
||||
#endif
|
||||
[3] = { "null", 0666, &null_fops, 0 },
|
||||
[3] = { "null", 0666, &null_fops, FMODE_NOWAIT },
|
||||
#ifdef CONFIG_DEVPORT
|
||||
[4] = { "port", 0, &port_fops, 0 },
|
||||
#endif
|
||||
[5] = { "zero", 0666, &zero_fops, 0 },
|
||||
[5] = { "zero", 0666, &zero_fops, FMODE_NOWAIT },
|
||||
[7] = { "full", 0666, &full_fops, 0 },
|
||||
[8] = { "random", 0666, &random_fops, 0 },
|
||||
[9] = { "urandom", 0666, &urandom_fops, 0 },
|
||||
|
|
|
@ -87,13 +87,8 @@ struct xilly_channel {
|
|||
};
|
||||
|
||||
struct xilly_endpoint {
|
||||
/*
|
||||
* One of pdev and dev is always NULL, and the other is a valid
|
||||
* pointer, depending on the type of device
|
||||
*/
|
||||
struct pci_dev *pdev;
|
||||
struct device *dev;
|
||||
struct xilly_endpoint_hardware *ephw;
|
||||
struct module *owner;
|
||||
|
||||
int dma_using_dac; /* =1 if 64-bit DMA is used, =0 otherwise. */
|
||||
__iomem void *registers;
|
||||
|
@ -113,25 +108,8 @@ struct xilly_endpoint {
|
|||
unsigned int msg_buf_size;
|
||||
};
|
||||
|
||||
struct xilly_endpoint_hardware {
|
||||
struct module *owner;
|
||||
void (*hw_sync_sgl_for_cpu)(struct xilly_endpoint *,
|
||||
dma_addr_t,
|
||||
size_t,
|
||||
int);
|
||||
void (*hw_sync_sgl_for_device)(struct xilly_endpoint *,
|
||||
dma_addr_t,
|
||||
size_t,
|
||||
int);
|
||||
int (*map_single)(struct xilly_endpoint *,
|
||||
void *,
|
||||
size_t,
|
||||
int,
|
||||
dma_addr_t *);
|
||||
};
|
||||
|
||||
struct xilly_mapping {
|
||||
void *device;
|
||||
struct device *device;
|
||||
dma_addr_t dma_addr;
|
||||
size_t size;
|
||||
int direction;
|
||||
|
@ -139,10 +117,7 @@ struct xilly_mapping {
|
|||
|
||||
irqreturn_t xillybus_isr(int irq, void *data);
|
||||
|
||||
struct xilly_endpoint *xillybus_init_endpoint(struct pci_dev *pdev,
|
||||
struct device *dev,
|
||||
struct xilly_endpoint_hardware
|
||||
*ephw);
|
||||
struct xilly_endpoint *xillybus_init_endpoint(struct device *dev);
|
||||
|
||||
int xillybus_endpoint_discovery(struct xilly_endpoint *endpoint);
|
||||
|
||||
|
|
|
@ -122,10 +122,8 @@ irqreturn_t xillybus_isr(int irq, void *data)
|
|||
buf = ep->msgbuf_addr;
|
||||
buf_size = ep->msg_buf_size/sizeof(u32);
|
||||
|
||||
ep->ephw->hw_sync_sgl_for_cpu(ep,
|
||||
ep->msgbuf_dma_addr,
|
||||
ep->msg_buf_size,
|
||||
DMA_FROM_DEVICE);
|
||||
dma_sync_single_for_cpu(ep->dev, ep->msgbuf_dma_addr,
|
||||
ep->msg_buf_size, DMA_FROM_DEVICE);
|
||||
|
||||
for (i = 0; i < buf_size; i += 2) {
|
||||
if (((buf[i+1] >> 28) & 0xf) != ep->msg_counter) {
|
||||
|
@ -140,11 +138,10 @@ irqreturn_t xillybus_isr(int irq, void *data)
|
|||
dev_err(ep->dev,
|
||||
"Lost sync with interrupt messages. Stopping.\n");
|
||||
} else {
|
||||
ep->ephw->hw_sync_sgl_for_device(
|
||||
ep,
|
||||
ep->msgbuf_dma_addr,
|
||||
ep->msg_buf_size,
|
||||
DMA_FROM_DEVICE);
|
||||
dma_sync_single_for_device(ep->dev,
|
||||
ep->msgbuf_dma_addr,
|
||||
ep->msg_buf_size,
|
||||
DMA_FROM_DEVICE);
|
||||
|
||||
iowrite32(0x01, /* Message NACK */
|
||||
ep->registers + fpga_msg_ctrl_reg);
|
||||
|
@ -275,10 +272,8 @@ irqreturn_t xillybus_isr(int irq, void *data)
|
|||
}
|
||||
}
|
||||
|
||||
ep->ephw->hw_sync_sgl_for_device(ep,
|
||||
ep->msgbuf_dma_addr,
|
||||
ep->msg_buf_size,
|
||||
DMA_FROM_DEVICE);
|
||||
dma_sync_single_for_device(ep->dev, ep->msgbuf_dma_addr,
|
||||
ep->msg_buf_size, DMA_FROM_DEVICE);
|
||||
|
||||
ep->msg_counter = (ep->msg_counter + 1) & 0xf;
|
||||
ep->failed_messages = 0;
|
||||
|
@ -304,6 +299,47 @@ struct xilly_alloc_state {
|
|||
u32 regdirection;
|
||||
};
|
||||
|
||||
static void xilly_unmap(void *ptr)
|
||||
{
|
||||
struct xilly_mapping *data = ptr;
|
||||
|
||||
dma_unmap_single(data->device, data->dma_addr,
|
||||
data->size, data->direction);
|
||||
|
||||
kfree(ptr);
|
||||
}
|
||||
|
||||
static int xilly_map_single(struct xilly_endpoint *ep,
|
||||
void *ptr,
|
||||
size_t size,
|
||||
int direction,
|
||||
dma_addr_t *ret_dma_handle
|
||||
)
|
||||
{
|
||||
dma_addr_t addr;
|
||||
struct xilly_mapping *this;
|
||||
|
||||
this = kzalloc(sizeof(*this), GFP_KERNEL);
|
||||
if (!this)
|
||||
return -ENOMEM;
|
||||
|
||||
addr = dma_map_single(ep->dev, ptr, size, direction);
|
||||
|
||||
if (dma_mapping_error(ep->dev, addr)) {
|
||||
kfree(this);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
this->device = ep->dev;
|
||||
this->dma_addr = addr;
|
||||
this->size = size;
|
||||
this->direction = direction;
|
||||
|
||||
*ret_dma_handle = addr;
|
||||
|
||||
return devm_add_action_or_reset(ep->dev, xilly_unmap, this);
|
||||
}
|
||||
|
||||
static int xilly_get_dma_buffers(struct xilly_endpoint *ep,
|
||||
struct xilly_alloc_state *s,
|
||||
struct xilly_buffer **buffers,
|
||||
|
@ -355,9 +391,9 @@ static int xilly_get_dma_buffers(struct xilly_endpoint *ep,
|
|||
s->left_of_salami = allocsize;
|
||||
}
|
||||
|
||||
rc = ep->ephw->map_single(ep, s->salami,
|
||||
bytebufsize, s->direction,
|
||||
&dma_addr);
|
||||
rc = xilly_map_single(ep, s->salami,
|
||||
bytebufsize, s->direction,
|
||||
&dma_addr);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
|
@ -620,11 +656,10 @@ static int xilly_obtain_idt(struct xilly_endpoint *endpoint)
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
endpoint->ephw->hw_sync_sgl_for_cpu(
|
||||
channel->endpoint,
|
||||
channel->wr_buffers[0]->dma_addr,
|
||||
channel->wr_buf_size,
|
||||
DMA_FROM_DEVICE);
|
||||
dma_sync_single_for_cpu(channel->endpoint->dev,
|
||||
channel->wr_buffers[0]->dma_addr,
|
||||
channel->wr_buf_size,
|
||||
DMA_FROM_DEVICE);
|
||||
|
||||
if (channel->wr_buffers[0]->end_offset != endpoint->idtlen) {
|
||||
dev_err(endpoint->dev,
|
||||
|
@ -735,11 +770,10 @@ static ssize_t xillybus_read(struct file *filp, char __user *userbuf,
|
|||
if (!empty) { /* Go on, now without the spinlock */
|
||||
|
||||
if (bufpos == 0) /* Position zero means it's virgin */
|
||||
channel->endpoint->ephw->hw_sync_sgl_for_cpu(
|
||||
channel->endpoint,
|
||||
channel->wr_buffers[bufidx]->dma_addr,
|
||||
channel->wr_buf_size,
|
||||
DMA_FROM_DEVICE);
|
||||
dma_sync_single_for_cpu(channel->endpoint->dev,
|
||||
channel->wr_buffers[bufidx]->dma_addr,
|
||||
channel->wr_buf_size,
|
||||
DMA_FROM_DEVICE);
|
||||
|
||||
if (copy_to_user(
|
||||
userbuf,
|
||||
|
@ -751,11 +785,10 @@ static ssize_t xillybus_read(struct file *filp, char __user *userbuf,
|
|||
bytes_done += howmany;
|
||||
|
||||
if (bufferdone) {
|
||||
channel->endpoint->ephw->hw_sync_sgl_for_device(
|
||||
channel->endpoint,
|
||||
channel->wr_buffers[bufidx]->dma_addr,
|
||||
channel->wr_buf_size,
|
||||
DMA_FROM_DEVICE);
|
||||
dma_sync_single_for_device(channel->endpoint->dev,
|
||||
channel->wr_buffers[bufidx]->dma_addr,
|
||||
channel->wr_buf_size,
|
||||
DMA_FROM_DEVICE);
|
||||
|
||||
/*
|
||||
* Tell FPGA the buffer is done with. It's an
|
||||
|
@ -1055,11 +1088,10 @@ static int xillybus_myflush(struct xilly_channel *channel, long timeout)
|
|||
else
|
||||
channel->rd_host_buf_idx++;
|
||||
|
||||
channel->endpoint->ephw->hw_sync_sgl_for_device(
|
||||
channel->endpoint,
|
||||
channel->rd_buffers[bufidx]->dma_addr,
|
||||
channel->rd_buf_size,
|
||||
DMA_TO_DEVICE);
|
||||
dma_sync_single_for_device(channel->endpoint->dev,
|
||||
channel->rd_buffers[bufidx]->dma_addr,
|
||||
channel->rd_buf_size,
|
||||
DMA_TO_DEVICE);
|
||||
|
||||
mutex_lock(&channel->endpoint->register_mutex);
|
||||
|
||||
|
@ -1275,11 +1307,10 @@ static ssize_t xillybus_write(struct file *filp, const char __user *userbuf,
|
|||
|
||||
if ((bufpos == 0) || /* Zero means it's virgin */
|
||||
(channel->rd_leftovers[3] != 0)) {
|
||||
channel->endpoint->ephw->hw_sync_sgl_for_cpu(
|
||||
channel->endpoint,
|
||||
channel->rd_buffers[bufidx]->dma_addr,
|
||||
channel->rd_buf_size,
|
||||
DMA_TO_DEVICE);
|
||||
dma_sync_single_for_cpu(channel->endpoint->dev,
|
||||
channel->rd_buffers[bufidx]->dma_addr,
|
||||
channel->rd_buf_size,
|
||||
DMA_TO_DEVICE);
|
||||
|
||||
/* Virgin, but leftovers are due */
|
||||
for (i = 0; i < bufpos; i++)
|
||||
|
@ -1297,11 +1328,10 @@ static ssize_t xillybus_write(struct file *filp, const char __user *userbuf,
|
|||
bytes_done += howmany;
|
||||
|
||||
if (bufferdone) {
|
||||
channel->endpoint->ephw->hw_sync_sgl_for_device(
|
||||
channel->endpoint,
|
||||
channel->rd_buffers[bufidx]->dma_addr,
|
||||
channel->rd_buf_size,
|
||||
DMA_TO_DEVICE);
|
||||
dma_sync_single_for_device(channel->endpoint->dev,
|
||||
channel->rd_buffers[bufidx]->dma_addr,
|
||||
channel->rd_buf_size,
|
||||
DMA_TO_DEVICE);
|
||||
|
||||
mutex_lock(&channel->endpoint->register_mutex);
|
||||
|
||||
|
@ -1772,10 +1802,7 @@ static const struct file_operations xillybus_fops = {
|
|||
.poll = xillybus_poll,
|
||||
};
|
||||
|
||||
struct xilly_endpoint *xillybus_init_endpoint(struct pci_dev *pdev,
|
||||
struct device *dev,
|
||||
struct xilly_endpoint_hardware
|
||||
*ephw)
|
||||
struct xilly_endpoint *xillybus_init_endpoint(struct device *dev)
|
||||
{
|
||||
struct xilly_endpoint *endpoint;
|
||||
|
||||
|
@ -1783,9 +1810,7 @@ struct xilly_endpoint *xillybus_init_endpoint(struct pci_dev *pdev,
|
|||
if (!endpoint)
|
||||
return NULL;
|
||||
|
||||
endpoint->pdev = pdev;
|
||||
endpoint->dev = dev;
|
||||
endpoint->ephw = ephw;
|
||||
endpoint->msg_counter = 0x0b;
|
||||
endpoint->failed_messages = 0;
|
||||
endpoint->fatal_error = 0;
|
||||
|
@ -1912,7 +1937,7 @@ int xillybus_endpoint_discovery(struct xilly_endpoint *endpoint)
|
|||
goto failed_idt;
|
||||
|
||||
rc = xillybus_init_chrdev(dev, &xillybus_fops,
|
||||
endpoint->ephw->owner, endpoint,
|
||||
endpoint->owner, endpoint,
|
||||
idt_handle.names,
|
||||
idt_handle.names_len,
|
||||
endpoint->num_channels,
|
||||
|
|
|
@ -31,102 +31,22 @@ static const struct of_device_id xillybus_of_match[] = {
|
|||
|
||||
MODULE_DEVICE_TABLE(of, xillybus_of_match);
|
||||
|
||||
static void xilly_dma_sync_single_for_cpu_of(struct xilly_endpoint *ep,
|
||||
dma_addr_t dma_handle,
|
||||
size_t size,
|
||||
int direction)
|
||||
{
|
||||
dma_sync_single_for_cpu(ep->dev, dma_handle, size, direction);
|
||||
}
|
||||
|
||||
static void xilly_dma_sync_single_for_device_of(struct xilly_endpoint *ep,
|
||||
dma_addr_t dma_handle,
|
||||
size_t size,
|
||||
int direction)
|
||||
{
|
||||
dma_sync_single_for_device(ep->dev, dma_handle, size, direction);
|
||||
}
|
||||
|
||||
static void xilly_dma_sync_single_nop(struct xilly_endpoint *ep,
|
||||
dma_addr_t dma_handle,
|
||||
size_t size,
|
||||
int direction)
|
||||
{
|
||||
}
|
||||
|
||||
static void xilly_of_unmap(void *ptr)
|
||||
{
|
||||
struct xilly_mapping *data = ptr;
|
||||
|
||||
dma_unmap_single(data->device, data->dma_addr,
|
||||
data->size, data->direction);
|
||||
|
||||
kfree(ptr);
|
||||
}
|
||||
|
||||
static int xilly_map_single_of(struct xilly_endpoint *ep,
|
||||
void *ptr,
|
||||
size_t size,
|
||||
int direction,
|
||||
dma_addr_t *ret_dma_handle
|
||||
)
|
||||
{
|
||||
dma_addr_t addr;
|
||||
struct xilly_mapping *this;
|
||||
|
||||
this = kzalloc(sizeof(*this), GFP_KERNEL);
|
||||
if (!this)
|
||||
return -ENOMEM;
|
||||
|
||||
addr = dma_map_single(ep->dev, ptr, size, direction);
|
||||
|
||||
if (dma_mapping_error(ep->dev, addr)) {
|
||||
kfree(this);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
this->device = ep->dev;
|
||||
this->dma_addr = addr;
|
||||
this->size = size;
|
||||
this->direction = direction;
|
||||
|
||||
*ret_dma_handle = addr;
|
||||
|
||||
return devm_add_action_or_reset(ep->dev, xilly_of_unmap, this);
|
||||
}
|
||||
|
||||
static struct xilly_endpoint_hardware of_hw = {
|
||||
.owner = THIS_MODULE,
|
||||
.hw_sync_sgl_for_cpu = xilly_dma_sync_single_for_cpu_of,
|
||||
.hw_sync_sgl_for_device = xilly_dma_sync_single_for_device_of,
|
||||
.map_single = xilly_map_single_of,
|
||||
};
|
||||
|
||||
static struct xilly_endpoint_hardware of_hw_coherent = {
|
||||
.owner = THIS_MODULE,
|
||||
.hw_sync_sgl_for_cpu = xilly_dma_sync_single_nop,
|
||||
.hw_sync_sgl_for_device = xilly_dma_sync_single_nop,
|
||||
.map_single = xilly_map_single_of,
|
||||
};
|
||||
|
||||
static int xilly_drv_probe(struct platform_device *op)
|
||||
{
|
||||
struct device *dev = &op->dev;
|
||||
struct xilly_endpoint *endpoint;
|
||||
int rc;
|
||||
int irq;
|
||||
struct xilly_endpoint_hardware *ephw = &of_hw;
|
||||
|
||||
if (of_property_read_bool(dev->of_node, "dma-coherent"))
|
||||
ephw = &of_hw_coherent;
|
||||
|
||||
endpoint = xillybus_init_endpoint(NULL, dev, ephw);
|
||||
endpoint = xillybus_init_endpoint(dev);
|
||||
|
||||
if (!endpoint)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(dev, endpoint);
|
||||
|
||||
endpoint->owner = THIS_MODULE;
|
||||
|
||||
endpoint->registers = devm_platform_ioremap_resource(op, 0);
|
||||
if (IS_ERR(endpoint->registers))
|
||||
return PTR_ERR(endpoint->registers);
|
||||
|
|
|
@ -32,110 +32,21 @@ static const struct pci_device_id xillyids[] = {
|
|||
{ /* End: all zeroes */ }
|
||||
};
|
||||
|
||||
static int xilly_pci_direction(int direction)
|
||||
{
|
||||
switch (direction) {
|
||||
case DMA_TO_DEVICE:
|
||||
return PCI_DMA_TODEVICE;
|
||||
case DMA_FROM_DEVICE:
|
||||
return PCI_DMA_FROMDEVICE;
|
||||
default:
|
||||
return PCI_DMA_BIDIRECTIONAL;
|
||||
}
|
||||
}
|
||||
|
||||
static void xilly_dma_sync_single_for_cpu_pci(struct xilly_endpoint *ep,
|
||||
dma_addr_t dma_handle,
|
||||
size_t size,
|
||||
int direction)
|
||||
{
|
||||
pci_dma_sync_single_for_cpu(ep->pdev,
|
||||
dma_handle,
|
||||
size,
|
||||
xilly_pci_direction(direction));
|
||||
}
|
||||
|
||||
static void xilly_dma_sync_single_for_device_pci(struct xilly_endpoint *ep,
|
||||
dma_addr_t dma_handle,
|
||||
size_t size,
|
||||
int direction)
|
||||
{
|
||||
pci_dma_sync_single_for_device(ep->pdev,
|
||||
dma_handle,
|
||||
size,
|
||||
xilly_pci_direction(direction));
|
||||
}
|
||||
|
||||
static void xilly_pci_unmap(void *ptr)
|
||||
{
|
||||
struct xilly_mapping *data = ptr;
|
||||
|
||||
pci_unmap_single(data->device, data->dma_addr,
|
||||
data->size, data->direction);
|
||||
|
||||
kfree(ptr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Map either through the PCI DMA mapper or the non_PCI one. Behind the
|
||||
* scenes exactly the same functions are called with the same parameters,
|
||||
* but that can change.
|
||||
*/
|
||||
|
||||
static int xilly_map_single_pci(struct xilly_endpoint *ep,
|
||||
void *ptr,
|
||||
size_t size,
|
||||
int direction,
|
||||
dma_addr_t *ret_dma_handle
|
||||
)
|
||||
{
|
||||
int pci_direction;
|
||||
dma_addr_t addr;
|
||||
struct xilly_mapping *this;
|
||||
|
||||
this = kzalloc(sizeof(*this), GFP_KERNEL);
|
||||
if (!this)
|
||||
return -ENOMEM;
|
||||
|
||||
pci_direction = xilly_pci_direction(direction);
|
||||
|
||||
addr = pci_map_single(ep->pdev, ptr, size, pci_direction);
|
||||
|
||||
if (pci_dma_mapping_error(ep->pdev, addr)) {
|
||||
kfree(this);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
this->device = ep->pdev;
|
||||
this->dma_addr = addr;
|
||||
this->size = size;
|
||||
this->direction = pci_direction;
|
||||
|
||||
*ret_dma_handle = addr;
|
||||
|
||||
return devm_add_action_or_reset(ep->dev, xilly_pci_unmap, this);
|
||||
}
|
||||
|
||||
static struct xilly_endpoint_hardware pci_hw = {
|
||||
.owner = THIS_MODULE,
|
||||
.hw_sync_sgl_for_cpu = xilly_dma_sync_single_for_cpu_pci,
|
||||
.hw_sync_sgl_for_device = xilly_dma_sync_single_for_device_pci,
|
||||
.map_single = xilly_map_single_pci,
|
||||
};
|
||||
|
||||
static int xilly_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *ent)
|
||||
{
|
||||
struct xilly_endpoint *endpoint;
|
||||
int rc;
|
||||
|
||||
endpoint = xillybus_init_endpoint(pdev, &pdev->dev, &pci_hw);
|
||||
endpoint = xillybus_init_endpoint(&pdev->dev);
|
||||
|
||||
if (!endpoint)
|
||||
return -ENOMEM;
|
||||
|
||||
pci_set_drvdata(pdev, endpoint);
|
||||
|
||||
endpoint->owner = THIS_MODULE;
|
||||
|
||||
rc = pcim_enable_device(pdev);
|
||||
if (rc) {
|
||||
dev_err(endpoint->dev,
|
||||
|
@ -185,9 +96,9 @@ static int xilly_probe(struct pci_dev *pdev,
|
|||
* So go for the 64-bit mask only when failing is the other option.
|
||||
*/
|
||||
|
||||
if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
|
||||
if (!dma_set_mask(&pdev->dev, DMA_BIT_MASK(32))) {
|
||||
endpoint->dma_using_dac = 0;
|
||||
} else if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) {
|
||||
} else if (!dma_set_mask(&pdev->dev, DMA_BIT_MASK(64))) {
|
||||
endpoint->dma_using_dac = 1;
|
||||
} else {
|
||||
dev_err(endpoint->dev, "Failed to set DMA mask. Aborting.\n");
|
||||
|
|
|
@ -1912,6 +1912,7 @@ static int xillyusb_setup_base_eps(struct xillyusb_dev *xdev)
|
|||
|
||||
dealloc:
|
||||
endpoint_dealloc(xdev->msg_ep); /* Also frees FIFO mem if allocated */
|
||||
xdev->msg_ep = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include "../comedi_usb.h"
|
||||
|
@ -237,22 +238,42 @@ static int dt9812_read_info(struct comedi_device *dev,
|
|||
{
|
||||
struct usb_device *usb = comedi_to_usb_dev(dev);
|
||||
struct dt9812_private *devpriv = dev->private;
|
||||
struct dt9812_usb_cmd cmd;
|
||||
struct dt9812_usb_cmd *cmd;
|
||||
size_t tbuf_size;
|
||||
int count, ret;
|
||||
void *tbuf;
|
||||
|
||||
cmd.cmd = cpu_to_le32(DT9812_R_FLASH_DATA);
|
||||
cmd.u.flash_data_info.address =
|
||||
tbuf_size = max(sizeof(*cmd), buf_size);
|
||||
|
||||
tbuf = kzalloc(tbuf_size, GFP_KERNEL);
|
||||
if (!tbuf)
|
||||
return -ENOMEM;
|
||||
|
||||
cmd = tbuf;
|
||||
|
||||
cmd->cmd = cpu_to_le32(DT9812_R_FLASH_DATA);
|
||||
cmd->u.flash_data_info.address =
|
||||
cpu_to_le16(DT9812_DIAGS_BOARD_INFO_ADDR + offset);
|
||||
cmd.u.flash_data_info.numbytes = cpu_to_le16(buf_size);
|
||||
cmd->u.flash_data_info.numbytes = cpu_to_le16(buf_size);
|
||||
|
||||
/* DT9812 only responds to 32 byte writes!! */
|
||||
ret = usb_bulk_msg(usb, usb_sndbulkpipe(usb, devpriv->cmd_wr.addr),
|
||||
&cmd, 32, &count, DT9812_USB_TIMEOUT);
|
||||
cmd, sizeof(*cmd), &count, DT9812_USB_TIMEOUT);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto out;
|
||||
|
||||
return usb_bulk_msg(usb, usb_rcvbulkpipe(usb, devpriv->cmd_rd.addr),
|
||||
buf, buf_size, &count, DT9812_USB_TIMEOUT);
|
||||
ret = usb_bulk_msg(usb, usb_rcvbulkpipe(usb, devpriv->cmd_rd.addr),
|
||||
tbuf, buf_size, &count, DT9812_USB_TIMEOUT);
|
||||
if (!ret) {
|
||||
if (count == buf_size)
|
||||
memcpy(buf, tbuf, buf_size);
|
||||
else
|
||||
ret = -EREMOTEIO;
|
||||
}
|
||||
out:
|
||||
kfree(tbuf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dt9812_read_multiple_registers(struct comedi_device *dev,
|
||||
|
@ -261,22 +282,42 @@ static int dt9812_read_multiple_registers(struct comedi_device *dev,
|
|||
{
|
||||
struct usb_device *usb = comedi_to_usb_dev(dev);
|
||||
struct dt9812_private *devpriv = dev->private;
|
||||
struct dt9812_usb_cmd cmd;
|
||||
struct dt9812_usb_cmd *cmd;
|
||||
int i, count, ret;
|
||||
size_t buf_size;
|
||||
void *buf;
|
||||
|
||||
cmd.cmd = cpu_to_le32(DT9812_R_MULTI_BYTE_REG);
|
||||
cmd.u.read_multi_info.count = reg_count;
|
||||
buf_size = max_t(size_t, sizeof(*cmd), reg_count);
|
||||
|
||||
buf = kzalloc(buf_size, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
cmd = buf;
|
||||
|
||||
cmd->cmd = cpu_to_le32(DT9812_R_MULTI_BYTE_REG);
|
||||
cmd->u.read_multi_info.count = reg_count;
|
||||
for (i = 0; i < reg_count; i++)
|
||||
cmd.u.read_multi_info.address[i] = address[i];
|
||||
cmd->u.read_multi_info.address[i] = address[i];
|
||||
|
||||
/* DT9812 only responds to 32 byte writes!! */
|
||||
ret = usb_bulk_msg(usb, usb_sndbulkpipe(usb, devpriv->cmd_wr.addr),
|
||||
&cmd, 32, &count, DT9812_USB_TIMEOUT);
|
||||
cmd, sizeof(*cmd), &count, DT9812_USB_TIMEOUT);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto out;
|
||||
|
||||
return usb_bulk_msg(usb, usb_rcvbulkpipe(usb, devpriv->cmd_rd.addr),
|
||||
value, reg_count, &count, DT9812_USB_TIMEOUT);
|
||||
ret = usb_bulk_msg(usb, usb_rcvbulkpipe(usb, devpriv->cmd_rd.addr),
|
||||
buf, reg_count, &count, DT9812_USB_TIMEOUT);
|
||||
if (!ret) {
|
||||
if (count == reg_count)
|
||||
memcpy(value, buf, reg_count);
|
||||
else
|
||||
ret = -EREMOTEIO;
|
||||
}
|
||||
out:
|
||||
kfree(buf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dt9812_write_multiple_registers(struct comedi_device *dev,
|
||||
|
@ -285,19 +326,27 @@ static int dt9812_write_multiple_registers(struct comedi_device *dev,
|
|||
{
|
||||
struct usb_device *usb = comedi_to_usb_dev(dev);
|
||||
struct dt9812_private *devpriv = dev->private;
|
||||
struct dt9812_usb_cmd cmd;
|
||||
struct dt9812_usb_cmd *cmd;
|
||||
int i, count;
|
||||
int ret;
|
||||
|
||||
cmd.cmd = cpu_to_le32(DT9812_W_MULTI_BYTE_REG);
|
||||
cmd.u.read_multi_info.count = reg_count;
|
||||
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
|
||||
if (!cmd)
|
||||
return -ENOMEM;
|
||||
|
||||
cmd->cmd = cpu_to_le32(DT9812_W_MULTI_BYTE_REG);
|
||||
cmd->u.read_multi_info.count = reg_count;
|
||||
for (i = 0; i < reg_count; i++) {
|
||||
cmd.u.write_multi_info.write[i].address = address[i];
|
||||
cmd.u.write_multi_info.write[i].value = value[i];
|
||||
cmd->u.write_multi_info.write[i].address = address[i];
|
||||
cmd->u.write_multi_info.write[i].value = value[i];
|
||||
}
|
||||
|
||||
/* DT9812 only responds to 32 byte writes!! */
|
||||
return usb_bulk_msg(usb, usb_sndbulkpipe(usb, devpriv->cmd_wr.addr),
|
||||
&cmd, 32, &count, DT9812_USB_TIMEOUT);
|
||||
ret = usb_bulk_msg(usb, usb_sndbulkpipe(usb, devpriv->cmd_wr.addr),
|
||||
cmd, sizeof(*cmd), &count, DT9812_USB_TIMEOUT);
|
||||
kfree(cmd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dt9812_rmw_multiple_registers(struct comedi_device *dev,
|
||||
|
@ -306,17 +355,25 @@ static int dt9812_rmw_multiple_registers(struct comedi_device *dev,
|
|||
{
|
||||
struct usb_device *usb = comedi_to_usb_dev(dev);
|
||||
struct dt9812_private *devpriv = dev->private;
|
||||
struct dt9812_usb_cmd cmd;
|
||||
struct dt9812_usb_cmd *cmd;
|
||||
int i, count;
|
||||
int ret;
|
||||
|
||||
cmd.cmd = cpu_to_le32(DT9812_RMW_MULTI_BYTE_REG);
|
||||
cmd.u.rmw_multi_info.count = reg_count;
|
||||
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
|
||||
if (!cmd)
|
||||
return -ENOMEM;
|
||||
|
||||
cmd->cmd = cpu_to_le32(DT9812_RMW_MULTI_BYTE_REG);
|
||||
cmd->u.rmw_multi_info.count = reg_count;
|
||||
for (i = 0; i < reg_count; i++)
|
||||
cmd.u.rmw_multi_info.rmw[i] = rmw[i];
|
||||
cmd->u.rmw_multi_info.rmw[i] = rmw[i];
|
||||
|
||||
/* DT9812 only responds to 32 byte writes!! */
|
||||
return usb_bulk_msg(usb, usb_sndbulkpipe(usb, devpriv->cmd_wr.addr),
|
||||
&cmd, 32, &count, DT9812_USB_TIMEOUT);
|
||||
ret = usb_bulk_msg(usb, usb_sndbulkpipe(usb, devpriv->cmd_wr.addr),
|
||||
cmd, sizeof(*cmd), &count, DT9812_USB_TIMEOUT);
|
||||
kfree(cmd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dt9812_digital_in(struct comedi_device *dev, u8 *bits)
|
||||
|
|
|
@ -144,6 +144,10 @@ static const u8 READ_COUNTER_RESPONSE[] = {0x00, 0x01, 0x00, 0x10,
|
|||
0x00, 0x00, 0x00, 0x02,
|
||||
0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
/* Largest supported packets */
|
||||
static const size_t TX_MAX_SIZE = sizeof(SET_PORT_DIR_REQUEST);
|
||||
static const size_t RX_MAX_SIZE = sizeof(READ_PORT_RESPONSE);
|
||||
|
||||
enum commands {
|
||||
READ_PORT,
|
||||
WRITE_PORT,
|
||||
|
@ -501,6 +505,12 @@ static int ni6501_find_endpoints(struct comedi_device *dev)
|
|||
if (!devpriv->ep_rx || !devpriv->ep_tx)
|
||||
return -ENODEV;
|
||||
|
||||
if (usb_endpoint_maxp(devpriv->ep_rx) < RX_MAX_SIZE)
|
||||
return -ENODEV;
|
||||
|
||||
if (usb_endpoint_maxp(devpriv->ep_tx) < TX_MAX_SIZE)
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -90,6 +90,9 @@ enum {
|
|||
#define IC3_VERSION BIT(0)
|
||||
#define IC6_VERSION BIT(1)
|
||||
|
||||
#define MIN_BUF_SIZE 64
|
||||
#define PACKET_TIMEOUT 10000 /* ms */
|
||||
|
||||
enum vmk80xx_model {
|
||||
VMK8055_MODEL,
|
||||
VMK8061_MODEL
|
||||
|
@ -157,22 +160,21 @@ static void vmk80xx_do_bulk_msg(struct comedi_device *dev)
|
|||
__u8 rx_addr;
|
||||
unsigned int tx_pipe;
|
||||
unsigned int rx_pipe;
|
||||
size_t size;
|
||||
size_t tx_size;
|
||||
size_t rx_size;
|
||||
|
||||
tx_addr = devpriv->ep_tx->bEndpointAddress;
|
||||
rx_addr = devpriv->ep_rx->bEndpointAddress;
|
||||
tx_pipe = usb_sndbulkpipe(usb, tx_addr);
|
||||
rx_pipe = usb_rcvbulkpipe(usb, rx_addr);
|
||||
tx_size = usb_endpoint_maxp(devpriv->ep_tx);
|
||||
rx_size = usb_endpoint_maxp(devpriv->ep_rx);
|
||||
|
||||
/*
|
||||
* The max packet size attributes of the K8061
|
||||
* input/output endpoints are identical
|
||||
*/
|
||||
size = usb_endpoint_maxp(devpriv->ep_tx);
|
||||
usb_bulk_msg(usb, tx_pipe, devpriv->usb_tx_buf, tx_size, NULL,
|
||||
PACKET_TIMEOUT);
|
||||
|
||||
usb_bulk_msg(usb, tx_pipe, devpriv->usb_tx_buf,
|
||||
size, NULL, devpriv->ep_tx->bInterval);
|
||||
usb_bulk_msg(usb, rx_pipe, devpriv->usb_rx_buf, size, NULL, HZ * 10);
|
||||
usb_bulk_msg(usb, rx_pipe, devpriv->usb_rx_buf, rx_size, NULL,
|
||||
PACKET_TIMEOUT);
|
||||
}
|
||||
|
||||
static int vmk80xx_read_packet(struct comedi_device *dev)
|
||||
|
@ -191,7 +193,7 @@ static int vmk80xx_read_packet(struct comedi_device *dev)
|
|||
pipe = usb_rcvintpipe(usb, ep->bEndpointAddress);
|
||||
return usb_interrupt_msg(usb, pipe, devpriv->usb_rx_buf,
|
||||
usb_endpoint_maxp(ep), NULL,
|
||||
HZ * 10);
|
||||
PACKET_TIMEOUT);
|
||||
}
|
||||
|
||||
static int vmk80xx_write_packet(struct comedi_device *dev, int cmd)
|
||||
|
@ -212,7 +214,7 @@ static int vmk80xx_write_packet(struct comedi_device *dev, int cmd)
|
|||
pipe = usb_sndintpipe(usb, ep->bEndpointAddress);
|
||||
return usb_interrupt_msg(usb, pipe, devpriv->usb_tx_buf,
|
||||
usb_endpoint_maxp(ep), NULL,
|
||||
HZ * 10);
|
||||
PACKET_TIMEOUT);
|
||||
}
|
||||
|
||||
static int vmk80xx_reset_device(struct comedi_device *dev)
|
||||
|
@ -678,12 +680,12 @@ static int vmk80xx_alloc_usb_buffers(struct comedi_device *dev)
|
|||
struct vmk80xx_private *devpriv = dev->private;
|
||||
size_t size;
|
||||
|
||||
size = usb_endpoint_maxp(devpriv->ep_rx);
|
||||
size = max(usb_endpoint_maxp(devpriv->ep_rx), MIN_BUF_SIZE);
|
||||
devpriv->usb_rx_buf = kzalloc(size, GFP_KERNEL);
|
||||
if (!devpriv->usb_rx_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
size = usb_endpoint_maxp(devpriv->ep_tx);
|
||||
size = max(usb_endpoint_maxp(devpriv->ep_rx), MIN_BUF_SIZE);
|
||||
devpriv->usb_tx_buf = kzalloc(size, GFP_KERNEL);
|
||||
if (!devpriv->usb_tx_buf)
|
||||
return -ENOMEM;
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -23,11 +23,11 @@ config 104_QUAD_8
|
|||
A counter's respective error flag may be cleared by performing a write
|
||||
operation on the respective count value attribute. Although the
|
||||
104-QUAD-8 counters have a 25-bit range, only the lower 24 bits may be
|
||||
set, either directly or via the counter's preset attribute. Interrupts
|
||||
are not supported by this driver.
|
||||
set, either directly or via the counter's preset attribute.
|
||||
|
||||
The base port addresses for the devices may be configured via the base
|
||||
array module parameter.
|
||||
array module parameter. The interrupt line numbers for the devices may
|
||||
be configured via the irq array module parameter.
|
||||
|
||||
config INTERRUPT_CNT
|
||||
tristate "Interrupt counter driver"
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#
|
||||
|
||||
obj-$(CONFIG_COUNTER) += counter.o
|
||||
counter-y := counter-core.o counter-sysfs.o counter-chrdev.o
|
||||
|
||||
obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o
|
||||
obj-$(CONFIG_INTERRUPT_CNT) += interrupt-cnt.o
|
||||
|
|
|
@ -0,0 +1,573 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Generic Counter character device interface
|
||||
* Copyright (C) 2020 William Breathitt Gray
|
||||
*/
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/counter.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/kfifo.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/nospec.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/timekeeping.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/wait.h>
|
||||
|
||||
#include "counter-chrdev.h"
|
||||
|
||||
struct counter_comp_node {
|
||||
struct list_head l;
|
||||
struct counter_component component;
|
||||
struct counter_comp comp;
|
||||
void *parent;
|
||||
};
|
||||
|
||||
#define counter_comp_read_is_equal(a, b) \
|
||||
(a.action_read == b.action_read || \
|
||||
a.device_u8_read == b.device_u8_read || \
|
||||
a.count_u8_read == b.count_u8_read || \
|
||||
a.signal_u8_read == b.signal_u8_read || \
|
||||
a.device_u32_read == b.device_u32_read || \
|
||||
a.count_u32_read == b.count_u32_read || \
|
||||
a.signal_u32_read == b.signal_u32_read || \
|
||||
a.device_u64_read == b.device_u64_read || \
|
||||
a.count_u64_read == b.count_u64_read || \
|
||||
a.signal_u64_read == b.signal_u64_read)
|
||||
|
||||
#define counter_comp_read_is_set(comp) \
|
||||
(comp.action_read || \
|
||||
comp.device_u8_read || \
|
||||
comp.count_u8_read || \
|
||||
comp.signal_u8_read || \
|
||||
comp.device_u32_read || \
|
||||
comp.count_u32_read || \
|
||||
comp.signal_u32_read || \
|
||||
comp.device_u64_read || \
|
||||
comp.count_u64_read || \
|
||||
comp.signal_u64_read)
|
||||
|
||||
static ssize_t counter_chrdev_read(struct file *filp, char __user *buf,
|
||||
size_t len, loff_t *f_ps)
|
||||
{
|
||||
struct counter_device *const counter = filp->private_data;
|
||||
int err;
|
||||
unsigned int copied;
|
||||
|
||||
if (!counter->ops)
|
||||
return -ENODEV;
|
||||
|
||||
if (len < sizeof(struct counter_event))
|
||||
return -EINVAL;
|
||||
|
||||
do {
|
||||
if (kfifo_is_empty(&counter->events)) {
|
||||
if (filp->f_flags & O_NONBLOCK)
|
||||
return -EAGAIN;
|
||||
|
||||
err = wait_event_interruptible(counter->events_wait,
|
||||
!kfifo_is_empty(&counter->events) ||
|
||||
!counter->ops);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (!counter->ops)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (mutex_lock_interruptible(&counter->events_out_lock))
|
||||
return -ERESTARTSYS;
|
||||
err = kfifo_to_user(&counter->events, buf, len, &copied);
|
||||
mutex_unlock(&counter->events_out_lock);
|
||||
if (err < 0)
|
||||
return err;
|
||||
} while (!copied);
|
||||
|
||||
return copied;
|
||||
}
|
||||
|
||||
static __poll_t counter_chrdev_poll(struct file *filp,
|
||||
struct poll_table_struct *pollt)
|
||||
{
|
||||
struct counter_device *const counter = filp->private_data;
|
||||
__poll_t events = 0;
|
||||
|
||||
if (!counter->ops)
|
||||
return events;
|
||||
|
||||
poll_wait(filp, &counter->events_wait, pollt);
|
||||
|
||||
if (!kfifo_is_empty(&counter->events))
|
||||
events = EPOLLIN | EPOLLRDNORM;
|
||||
|
||||
return events;
|
||||
}
|
||||
|
||||
static void counter_events_list_free(struct list_head *const events_list)
|
||||
{
|
||||
struct counter_event_node *p, *n;
|
||||
struct counter_comp_node *q, *o;
|
||||
|
||||
list_for_each_entry_safe(p, n, events_list, l) {
|
||||
/* Free associated component nodes */
|
||||
list_for_each_entry_safe(q, o, &p->comp_list, l) {
|
||||
list_del(&q->l);
|
||||
kfree(q);
|
||||
}
|
||||
|
||||
/* Free event node */
|
||||
list_del(&p->l);
|
||||
kfree(p);
|
||||
}
|
||||
}
|
||||
|
||||
static int counter_set_event_node(struct counter_device *const counter,
|
||||
struct counter_watch *const watch,
|
||||
const struct counter_comp_node *const cfg)
|
||||
{
|
||||
struct counter_event_node *event_node;
|
||||
int err = 0;
|
||||
struct counter_comp_node *comp_node;
|
||||
|
||||
/* Search for event in the list */
|
||||
list_for_each_entry(event_node, &counter->next_events_list, l)
|
||||
if (event_node->event == watch->event &&
|
||||
event_node->channel == watch->channel)
|
||||
break;
|
||||
|
||||
/* If event is not already in the list */
|
||||
if (&event_node->l == &counter->next_events_list) {
|
||||
/* Allocate new event node */
|
||||
event_node = kmalloc(sizeof(*event_node), GFP_KERNEL);
|
||||
if (!event_node)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Configure event node and add to the list */
|
||||
event_node->event = watch->event;
|
||||
event_node->channel = watch->channel;
|
||||
INIT_LIST_HEAD(&event_node->comp_list);
|
||||
list_add(&event_node->l, &counter->next_events_list);
|
||||
}
|
||||
|
||||
/* Check if component watch has already been set before */
|
||||
list_for_each_entry(comp_node, &event_node->comp_list, l)
|
||||
if (comp_node->parent == cfg->parent &&
|
||||
counter_comp_read_is_equal(comp_node->comp, cfg->comp)) {
|
||||
err = -EINVAL;
|
||||
goto exit_free_event_node;
|
||||
}
|
||||
|
||||
/* Allocate component node */
|
||||
comp_node = kmalloc(sizeof(*comp_node), GFP_KERNEL);
|
||||
if (!comp_node) {
|
||||
err = -ENOMEM;
|
||||
goto exit_free_event_node;
|
||||
}
|
||||
*comp_node = *cfg;
|
||||
|
||||
/* Add component node to event node */
|
||||
list_add_tail(&comp_node->l, &event_node->comp_list);
|
||||
|
||||
exit_free_event_node:
|
||||
/* Free event node if no one else is watching */
|
||||
if (list_empty(&event_node->comp_list)) {
|
||||
list_del(&event_node->l);
|
||||
kfree(event_node);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int counter_enable_events(struct counter_device *const counter)
|
||||
{
|
||||
unsigned long flags;
|
||||
int err = 0;
|
||||
|
||||
mutex_lock(&counter->n_events_list_lock);
|
||||
spin_lock_irqsave(&counter->events_list_lock, flags);
|
||||
|
||||
counter_events_list_free(&counter->events_list);
|
||||
list_replace_init(&counter->next_events_list,
|
||||
&counter->events_list);
|
||||
|
||||
if (counter->ops->events_configure)
|
||||
err = counter->ops->events_configure(counter);
|
||||
|
||||
spin_unlock_irqrestore(&counter->events_list_lock, flags);
|
||||
mutex_unlock(&counter->n_events_list_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int counter_disable_events(struct counter_device *const counter)
|
||||
{
|
||||
unsigned long flags;
|
||||
int err = 0;
|
||||
|
||||
spin_lock_irqsave(&counter->events_list_lock, flags);
|
||||
|
||||
counter_events_list_free(&counter->events_list);
|
||||
|
||||
if (counter->ops->events_configure)
|
||||
err = counter->ops->events_configure(counter);
|
||||
|
||||
spin_unlock_irqrestore(&counter->events_list_lock, flags);
|
||||
|
||||
mutex_lock(&counter->n_events_list_lock);
|
||||
|
||||
counter_events_list_free(&counter->next_events_list);
|
||||
|
||||
mutex_unlock(&counter->n_events_list_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int counter_add_watch(struct counter_device *const counter,
|
||||
const unsigned long arg)
|
||||
{
|
||||
void __user *const uwatch = (void __user *)arg;
|
||||
struct counter_watch watch;
|
||||
struct counter_comp_node comp_node = {};
|
||||
size_t parent, id;
|
||||
struct counter_comp *ext;
|
||||
size_t num_ext;
|
||||
int err = 0;
|
||||
|
||||
if (copy_from_user(&watch, uwatch, sizeof(watch)))
|
||||
return -EFAULT;
|
||||
|
||||
if (watch.component.type == COUNTER_COMPONENT_NONE)
|
||||
goto no_component;
|
||||
|
||||
parent = watch.component.parent;
|
||||
|
||||
/* Configure parent component info for comp node */
|
||||
switch (watch.component.scope) {
|
||||
case COUNTER_SCOPE_DEVICE:
|
||||
ext = counter->ext;
|
||||
num_ext = counter->num_ext;
|
||||
break;
|
||||
case COUNTER_SCOPE_SIGNAL:
|
||||
if (parent >= counter->num_signals)
|
||||
return -EINVAL;
|
||||
parent = array_index_nospec(parent, counter->num_signals);
|
||||
|
||||
comp_node.parent = counter->signals + parent;
|
||||
|
||||
ext = counter->signals[parent].ext;
|
||||
num_ext = counter->signals[parent].num_ext;
|
||||
break;
|
||||
case COUNTER_SCOPE_COUNT:
|
||||
if (parent >= counter->num_counts)
|
||||
return -EINVAL;
|
||||
parent = array_index_nospec(parent, counter->num_counts);
|
||||
|
||||
comp_node.parent = counter->counts + parent;
|
||||
|
||||
ext = counter->counts[parent].ext;
|
||||
num_ext = counter->counts[parent].num_ext;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
id = watch.component.id;
|
||||
|
||||
/* Configure component info for comp node */
|
||||
switch (watch.component.type) {
|
||||
case COUNTER_COMPONENT_SIGNAL:
|
||||
if (watch.component.scope != COUNTER_SCOPE_SIGNAL)
|
||||
return -EINVAL;
|
||||
|
||||
comp_node.comp.type = COUNTER_COMP_SIGNAL_LEVEL;
|
||||
comp_node.comp.signal_u32_read = counter->ops->signal_read;
|
||||
break;
|
||||
case COUNTER_COMPONENT_COUNT:
|
||||
if (watch.component.scope != COUNTER_SCOPE_COUNT)
|
||||
return -EINVAL;
|
||||
|
||||
comp_node.comp.type = COUNTER_COMP_U64;
|
||||
comp_node.comp.count_u64_read = counter->ops->count_read;
|
||||
break;
|
||||
case COUNTER_COMPONENT_FUNCTION:
|
||||
if (watch.component.scope != COUNTER_SCOPE_COUNT)
|
||||
return -EINVAL;
|
||||
|
||||
comp_node.comp.type = COUNTER_COMP_FUNCTION;
|
||||
comp_node.comp.count_u32_read = counter->ops->function_read;
|
||||
break;
|
||||
case COUNTER_COMPONENT_SYNAPSE_ACTION:
|
||||
if (watch.component.scope != COUNTER_SCOPE_COUNT)
|
||||
return -EINVAL;
|
||||
if (id >= counter->counts[parent].num_synapses)
|
||||
return -EINVAL;
|
||||
id = array_index_nospec(id, counter->counts[parent].num_synapses);
|
||||
|
||||
comp_node.comp.type = COUNTER_COMP_SYNAPSE_ACTION;
|
||||
comp_node.comp.action_read = counter->ops->action_read;
|
||||
comp_node.comp.priv = counter->counts[parent].synapses + id;
|
||||
break;
|
||||
case COUNTER_COMPONENT_EXTENSION:
|
||||
if (id >= num_ext)
|
||||
return -EINVAL;
|
||||
id = array_index_nospec(id, num_ext);
|
||||
|
||||
comp_node.comp = ext[id];
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!counter_comp_read_is_set(comp_node.comp))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
no_component:
|
||||
mutex_lock(&counter->n_events_list_lock);
|
||||
|
||||
if (counter->ops->watch_validate) {
|
||||
err = counter->ops->watch_validate(counter, &watch);
|
||||
if (err < 0)
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
comp_node.component = watch.component;
|
||||
|
||||
err = counter_set_event_node(counter, &watch, &comp_node);
|
||||
|
||||
err_exit:
|
||||
mutex_unlock(&counter->n_events_list_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static long counter_chrdev_ioctl(struct file *filp, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct counter_device *const counter = filp->private_data;
|
||||
int ret = -ENODEV;
|
||||
|
||||
mutex_lock(&counter->ops_exist_lock);
|
||||
|
||||
if (!counter->ops)
|
||||
goto out_unlock;
|
||||
|
||||
switch (cmd) {
|
||||
case COUNTER_ADD_WATCH_IOCTL:
|
||||
ret = counter_add_watch(counter, arg);
|
||||
break;
|
||||
case COUNTER_ENABLE_EVENTS_IOCTL:
|
||||
ret = counter_enable_events(counter);
|
||||
break;
|
||||
case COUNTER_DISABLE_EVENTS_IOCTL:
|
||||
ret = counter_disable_events(counter);
|
||||
break;
|
||||
default:
|
||||
ret = -ENOIOCTLCMD;
|
||||
break;
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&counter->ops_exist_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int counter_chrdev_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct counter_device *const counter = container_of(inode->i_cdev,
|
||||
typeof(*counter),
|
||||
chrdev);
|
||||
|
||||
get_device(&counter->dev);
|
||||
filp->private_data = counter;
|
||||
|
||||
return nonseekable_open(inode, filp);
|
||||
}
|
||||
|
||||
static int counter_chrdev_release(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct counter_device *const counter = filp->private_data;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&counter->ops_exist_lock);
|
||||
|
||||
if (!counter->ops) {
|
||||
/* Free any lingering held memory */
|
||||
counter_events_list_free(&counter->events_list);
|
||||
counter_events_list_free(&counter->next_events_list);
|
||||
ret = -ENODEV;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
ret = counter_disable_events(counter);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&counter->ops_exist_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&counter->ops_exist_lock);
|
||||
|
||||
put_device(&counter->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations counter_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = no_llseek,
|
||||
.read = counter_chrdev_read,
|
||||
.poll = counter_chrdev_poll,
|
||||
.unlocked_ioctl = counter_chrdev_ioctl,
|
||||
.open = counter_chrdev_open,
|
||||
.release = counter_chrdev_release,
|
||||
};
|
||||
|
||||
int counter_chrdev_add(struct counter_device *const counter)
|
||||
{
|
||||
/* Initialize Counter events lists */
|
||||
INIT_LIST_HEAD(&counter->events_list);
|
||||
INIT_LIST_HEAD(&counter->next_events_list);
|
||||
spin_lock_init(&counter->events_list_lock);
|
||||
mutex_init(&counter->n_events_list_lock);
|
||||
init_waitqueue_head(&counter->events_wait);
|
||||
spin_lock_init(&counter->events_in_lock);
|
||||
mutex_init(&counter->events_out_lock);
|
||||
|
||||
/* Initialize character device */
|
||||
cdev_init(&counter->chrdev, &counter_fops);
|
||||
|
||||
/* Allocate Counter events queue */
|
||||
return kfifo_alloc(&counter->events, 64, GFP_KERNEL);
|
||||
}
|
||||
|
||||
void counter_chrdev_remove(struct counter_device *const counter)
|
||||
{
|
||||
kfifo_free(&counter->events);
|
||||
}
|
||||
|
||||
static int counter_get_data(struct counter_device *const counter,
|
||||
const struct counter_comp_node *const comp_node,
|
||||
u64 *const value)
|
||||
{
|
||||
const struct counter_comp *const comp = &comp_node->comp;
|
||||
void *const parent = comp_node->parent;
|
||||
u8 value_u8 = 0;
|
||||
u32 value_u32 = 0;
|
||||
int ret;
|
||||
|
||||
if (comp_node->component.type == COUNTER_COMPONENT_NONE)
|
||||
return 0;
|
||||
|
||||
switch (comp->type) {
|
||||
case COUNTER_COMP_U8:
|
||||
case COUNTER_COMP_BOOL:
|
||||
switch (comp_node->component.scope) {
|
||||
case COUNTER_SCOPE_DEVICE:
|
||||
ret = comp->device_u8_read(counter, &value_u8);
|
||||
break;
|
||||
case COUNTER_SCOPE_SIGNAL:
|
||||
ret = comp->signal_u8_read(counter, parent, &value_u8);
|
||||
break;
|
||||
case COUNTER_SCOPE_COUNT:
|
||||
ret = comp->count_u8_read(counter, parent, &value_u8);
|
||||
break;
|
||||
}
|
||||
*value = value_u8;
|
||||
return ret;
|
||||
case COUNTER_COMP_SIGNAL_LEVEL:
|
||||
case COUNTER_COMP_FUNCTION:
|
||||
case COUNTER_COMP_ENUM:
|
||||
case COUNTER_COMP_COUNT_DIRECTION:
|
||||
case COUNTER_COMP_COUNT_MODE:
|
||||
switch (comp_node->component.scope) {
|
||||
case COUNTER_SCOPE_DEVICE:
|
||||
ret = comp->device_u32_read(counter, &value_u32);
|
||||
break;
|
||||
case COUNTER_SCOPE_SIGNAL:
|
||||
ret = comp->signal_u32_read(counter, parent,
|
||||
&value_u32);
|
||||
break;
|
||||
case COUNTER_SCOPE_COUNT:
|
||||
ret = comp->count_u32_read(counter, parent, &value_u32);
|
||||
break;
|
||||
}
|
||||
*value = value_u32;
|
||||
return ret;
|
||||
case COUNTER_COMP_U64:
|
||||
switch (comp_node->component.scope) {
|
||||
case COUNTER_SCOPE_DEVICE:
|
||||
return comp->device_u64_read(counter, value);
|
||||
case COUNTER_SCOPE_SIGNAL:
|
||||
return comp->signal_u64_read(counter, parent, value);
|
||||
case COUNTER_SCOPE_COUNT:
|
||||
return comp->count_u64_read(counter, parent, value);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case COUNTER_COMP_SYNAPSE_ACTION:
|
||||
ret = comp->action_read(counter, parent, comp->priv,
|
||||
&value_u32);
|
||||
*value = value_u32;
|
||||
return ret;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* counter_push_event - queue event for userspace reading
|
||||
* @counter: pointer to Counter structure
|
||||
* @event: triggered event
|
||||
* @channel: event channel
|
||||
*
|
||||
* Note: If no one is watching for the respective event, it is silently
|
||||
* discarded.
|
||||
*/
|
||||
void counter_push_event(struct counter_device *const counter, const u8 event,
|
||||
const u8 channel)
|
||||
{
|
||||
struct counter_event ev;
|
||||
unsigned int copied = 0;
|
||||
unsigned long flags;
|
||||
struct counter_event_node *event_node;
|
||||
struct counter_comp_node *comp_node;
|
||||
|
||||
ev.timestamp = ktime_get_ns();
|
||||
ev.watch.event = event;
|
||||
ev.watch.channel = channel;
|
||||
|
||||
/* Could be in an interrupt context, so use a spin lock */
|
||||
spin_lock_irqsave(&counter->events_list_lock, flags);
|
||||
|
||||
/* Search for event in the list */
|
||||
list_for_each_entry(event_node, &counter->events_list, l)
|
||||
if (event_node->event == event &&
|
||||
event_node->channel == channel)
|
||||
break;
|
||||
|
||||
/* If event is not in the list */
|
||||
if (&event_node->l == &counter->events_list)
|
||||
goto exit_early;
|
||||
|
||||
/* Read and queue relevant comp for userspace */
|
||||
list_for_each_entry(comp_node, &event_node->comp_list, l) {
|
||||
ev.watch.component = comp_node->component;
|
||||
ev.status = -counter_get_data(counter, comp_node, &ev.value);
|
||||
|
||||
copied += kfifo_in_spinlocked_noirqsave(&counter->events, &ev,
|
||||
1, &counter->events_in_lock);
|
||||
}
|
||||
|
||||
exit_early:
|
||||
spin_unlock_irqrestore(&counter->events_list_lock, flags);
|
||||
|
||||
if (copied)
|
||||
wake_up_poll(&counter->events_wait, EPOLLIN);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(counter_push_event);
|
|
@ -0,0 +1,14 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Counter character device interface
|
||||
* Copyright (C) 2020 William Breathitt Gray
|
||||
*/
|
||||
#ifndef _COUNTER_CHRDEV_H_
|
||||
#define _COUNTER_CHRDEV_H_
|
||||
|
||||
#include <linux/counter.h>
|
||||
|
||||
int counter_chrdev_add(struct counter_device *const counter);
|
||||
void counter_chrdev_remove(struct counter_device *const counter);
|
||||
|
||||
#endif /* _COUNTER_CHRDEV_H_ */
|
|
@ -0,0 +1,191 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Generic Counter interface
|
||||
* Copyright (C) 2020 William Breathitt Gray
|
||||
*/
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/counter.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/device/bus.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kdev_t.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/wait.h>
|
||||
|
||||
#include "counter-chrdev.h"
|
||||
#include "counter-sysfs.h"
|
||||
|
||||
/* Provides a unique ID for each counter device */
|
||||
static DEFINE_IDA(counter_ida);
|
||||
|
||||
static void counter_device_release(struct device *dev)
|
||||
{
|
||||
struct counter_device *const counter = dev_get_drvdata(dev);
|
||||
|
||||
counter_chrdev_remove(counter);
|
||||
ida_free(&counter_ida, dev->id);
|
||||
}
|
||||
|
||||
static struct device_type counter_device_type = {
|
||||
.name = "counter_device",
|
||||
.release = counter_device_release,
|
||||
};
|
||||
|
||||
static struct bus_type counter_bus_type = {
|
||||
.name = "counter",
|
||||
.dev_name = "counter",
|
||||
};
|
||||
|
||||
static dev_t counter_devt;
|
||||
|
||||
/**
|
||||
* counter_register - register Counter to the system
|
||||
* @counter: pointer to Counter to register
|
||||
*
|
||||
* This function registers a Counter to the system. A sysfs "counter" directory
|
||||
* will be created and populated with sysfs attributes correlating with the
|
||||
* Counter Signals, Synapses, and Counts respectively.
|
||||
*
|
||||
* RETURNS:
|
||||
* 0 on success, negative error number on failure.
|
||||
*/
|
||||
int counter_register(struct counter_device *const counter)
|
||||
{
|
||||
struct device *const dev = &counter->dev;
|
||||
int id;
|
||||
int err;
|
||||
|
||||
/* Acquire unique ID */
|
||||
id = ida_alloc(&counter_ida, GFP_KERNEL);
|
||||
if (id < 0)
|
||||
return id;
|
||||
|
||||
mutex_init(&counter->ops_exist_lock);
|
||||
|
||||
/* Configure device structure for Counter */
|
||||
dev->id = id;
|
||||
dev->type = &counter_device_type;
|
||||
dev->bus = &counter_bus_type;
|
||||
dev->devt = MKDEV(MAJOR(counter_devt), id);
|
||||
if (counter->parent) {
|
||||
dev->parent = counter->parent;
|
||||
dev->of_node = counter->parent->of_node;
|
||||
}
|
||||
device_initialize(dev);
|
||||
dev_set_drvdata(dev, counter);
|
||||
|
||||
err = counter_sysfs_add(counter);
|
||||
if (err < 0)
|
||||
goto err_free_id;
|
||||
|
||||
err = counter_chrdev_add(counter);
|
||||
if (err < 0)
|
||||
goto err_free_id;
|
||||
|
||||
err = cdev_device_add(&counter->chrdev, dev);
|
||||
if (err < 0)
|
||||
goto err_remove_chrdev;
|
||||
|
||||
return 0;
|
||||
|
||||
err_remove_chrdev:
|
||||
counter_chrdev_remove(counter);
|
||||
err_free_id:
|
||||
put_device(dev);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(counter_register);
|
||||
|
||||
/**
|
||||
* counter_unregister - unregister Counter from the system
|
||||
* @counter: pointer to Counter to unregister
|
||||
*
|
||||
* The Counter is unregistered from the system.
|
||||
*/
|
||||
void counter_unregister(struct counter_device *const counter)
|
||||
{
|
||||
if (!counter)
|
||||
return;
|
||||
|
||||
cdev_device_del(&counter->chrdev, &counter->dev);
|
||||
|
||||
mutex_lock(&counter->ops_exist_lock);
|
||||
|
||||
counter->ops = NULL;
|
||||
wake_up(&counter->events_wait);
|
||||
|
||||
mutex_unlock(&counter->ops_exist_lock);
|
||||
|
||||
put_device(&counter->dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(counter_unregister);
|
||||
|
||||
static void devm_counter_release(void *counter)
|
||||
{
|
||||
counter_unregister(counter);
|
||||
}
|
||||
|
||||
/**
|
||||
* devm_counter_register - Resource-managed counter_register
|
||||
* @dev: device to allocate counter_device for
|
||||
* @counter: pointer to Counter to register
|
||||
*
|
||||
* Managed counter_register. The Counter registered with this function is
|
||||
* automatically unregistered on driver detach. This function calls
|
||||
* counter_register internally. Refer to that function for more information.
|
||||
*
|
||||
* RETURNS:
|
||||
* 0 on success, negative error number on failure.
|
||||
*/
|
||||
int devm_counter_register(struct device *dev,
|
||||
struct counter_device *const counter)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = counter_register(counter);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return devm_add_action_or_reset(dev, devm_counter_release, counter);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_counter_register);
|
||||
|
||||
#define COUNTER_DEV_MAX 256
|
||||
|
||||
static int __init counter_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = bus_register(&counter_bus_type);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = alloc_chrdev_region(&counter_devt, 0, COUNTER_DEV_MAX, "counter");
|
||||
if (err < 0)
|
||||
goto err_unregister_bus;
|
||||
|
||||
return 0;
|
||||
|
||||
err_unregister_bus:
|
||||
bus_unregister(&counter_bus_type);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit counter_exit(void)
|
||||
{
|
||||
unregister_chrdev_region(counter_devt, COUNTER_DEV_MAX);
|
||||
bus_unregister(&counter_bus_type);
|
||||
}
|
||||
|
||||
subsys_initcall(counter_init);
|
||||
module_exit(counter_exit);
|
||||
|
||||
MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
|
||||
MODULE_DESCRIPTION("Generic Counter interface");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,959 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Generic Counter sysfs interface
|
||||
* Copyright (C) 2020 William Breathitt Gray
|
||||
*/
|
||||
#include <linux/counter.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/kfifo.h>
|
||||
#include <linux/kstrtox.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "counter-sysfs.h"
|
||||
|
||||
/**
|
||||
* struct counter_attribute - Counter sysfs attribute
|
||||
* @dev_attr: device attribute for sysfs
|
||||
* @l: node to add Counter attribute to attribute group list
|
||||
* @comp: Counter component callbacks and data
|
||||
* @scope: Counter scope of the attribute
|
||||
* @parent: pointer to the parent component
|
||||
*/
|
||||
struct counter_attribute {
|
||||
struct device_attribute dev_attr;
|
||||
struct list_head l;
|
||||
|
||||
struct counter_comp comp;
|
||||
enum counter_scope scope;
|
||||
void *parent;
|
||||
};
|
||||
|
||||
#define to_counter_attribute(_dev_attr) \
|
||||
container_of(_dev_attr, struct counter_attribute, dev_attr)
|
||||
|
||||
/**
|
||||
* struct counter_attribute_group - container for attribute group
|
||||
* @name: name of the attribute group
|
||||
* @attr_list: list to keep track of created attributes
|
||||
* @num_attr: number of attributes
|
||||
*/
|
||||
struct counter_attribute_group {
|
||||
const char *name;
|
||||
struct list_head attr_list;
|
||||
size_t num_attr;
|
||||
};
|
||||
|
||||
static const char *const counter_function_str[] = {
|
||||
[COUNTER_FUNCTION_INCREASE] = "increase",
|
||||
[COUNTER_FUNCTION_DECREASE] = "decrease",
|
||||
[COUNTER_FUNCTION_PULSE_DIRECTION] = "pulse-direction",
|
||||
[COUNTER_FUNCTION_QUADRATURE_X1_A] = "quadrature x1 a",
|
||||
[COUNTER_FUNCTION_QUADRATURE_X1_B] = "quadrature x1 b",
|
||||
[COUNTER_FUNCTION_QUADRATURE_X2_A] = "quadrature x2 a",
|
||||
[COUNTER_FUNCTION_QUADRATURE_X2_B] = "quadrature x2 b",
|
||||
[COUNTER_FUNCTION_QUADRATURE_X4] = "quadrature x4"
|
||||
};
|
||||
|
||||
static const char *const counter_signal_value_str[] = {
|
||||
[COUNTER_SIGNAL_LEVEL_LOW] = "low",
|
||||
[COUNTER_SIGNAL_LEVEL_HIGH] = "high"
|
||||
};
|
||||
|
||||
static const char *const counter_synapse_action_str[] = {
|
||||
[COUNTER_SYNAPSE_ACTION_NONE] = "none",
|
||||
[COUNTER_SYNAPSE_ACTION_RISING_EDGE] = "rising edge",
|
||||
[COUNTER_SYNAPSE_ACTION_FALLING_EDGE] = "falling edge",
|
||||
[COUNTER_SYNAPSE_ACTION_BOTH_EDGES] = "both edges"
|
||||
};
|
||||
|
||||
static const char *const counter_count_direction_str[] = {
|
||||
[COUNTER_COUNT_DIRECTION_FORWARD] = "forward",
|
||||
[COUNTER_COUNT_DIRECTION_BACKWARD] = "backward"
|
||||
};
|
||||
|
||||
static const char *const counter_count_mode_str[] = {
|
||||
[COUNTER_COUNT_MODE_NORMAL] = "normal",
|
||||
[COUNTER_COUNT_MODE_RANGE_LIMIT] = "range limit",
|
||||
[COUNTER_COUNT_MODE_NON_RECYCLE] = "non-recycle",
|
||||
[COUNTER_COUNT_MODE_MODULO_N] = "modulo-n"
|
||||
};
|
||||
|
||||
static ssize_t counter_comp_u8_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
const struct counter_attribute *const a = to_counter_attribute(attr);
|
||||
struct counter_device *const counter = dev_get_drvdata(dev);
|
||||
int err;
|
||||
u8 data = 0;
|
||||
|
||||
switch (a->scope) {
|
||||
case COUNTER_SCOPE_DEVICE:
|
||||
err = a->comp.device_u8_read(counter, &data);
|
||||
break;
|
||||
case COUNTER_SCOPE_SIGNAL:
|
||||
err = a->comp.signal_u8_read(counter, a->parent, &data);
|
||||
break;
|
||||
case COUNTER_SCOPE_COUNT:
|
||||
err = a->comp.count_u8_read(counter, a->parent, &data);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (a->comp.type == COUNTER_COMP_BOOL)
|
||||
/* data should already be boolean but ensure just to be safe */
|
||||
data = !!data;
|
||||
|
||||
return sysfs_emit(buf, "%u\n", (unsigned int)data);
|
||||
}
|
||||
|
||||
static ssize_t counter_comp_u8_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
const struct counter_attribute *const a = to_counter_attribute(attr);
|
||||
struct counter_device *const counter = dev_get_drvdata(dev);
|
||||
int err;
|
||||
bool bool_data = 0;
|
||||
u8 data = 0;
|
||||
|
||||
if (a->comp.type == COUNTER_COMP_BOOL) {
|
||||
err = kstrtobool(buf, &bool_data);
|
||||
data = bool_data;
|
||||
} else
|
||||
err = kstrtou8(buf, 0, &data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
switch (a->scope) {
|
||||
case COUNTER_SCOPE_DEVICE:
|
||||
err = a->comp.device_u8_write(counter, data);
|
||||
break;
|
||||
case COUNTER_SCOPE_SIGNAL:
|
||||
err = a->comp.signal_u8_write(counter, a->parent, data);
|
||||
break;
|
||||
case COUNTER_SCOPE_COUNT:
|
||||
err = a->comp.count_u8_write(counter, a->parent, data);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t counter_comp_u32_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
const struct counter_attribute *const a = to_counter_attribute(attr);
|
||||
struct counter_device *const counter = dev_get_drvdata(dev);
|
||||
const struct counter_available *const avail = a->comp.priv;
|
||||
int err;
|
||||
u32 data = 0;
|
||||
|
||||
switch (a->scope) {
|
||||
case COUNTER_SCOPE_DEVICE:
|
||||
err = a->comp.device_u32_read(counter, &data);
|
||||
break;
|
||||
case COUNTER_SCOPE_SIGNAL:
|
||||
err = a->comp.signal_u32_read(counter, a->parent, &data);
|
||||
break;
|
||||
case COUNTER_SCOPE_COUNT:
|
||||
if (a->comp.type == COUNTER_COMP_SYNAPSE_ACTION)
|
||||
err = a->comp.action_read(counter, a->parent,
|
||||
a->comp.priv, &data);
|
||||
else
|
||||
err = a->comp.count_u32_read(counter, a->parent, &data);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
switch (a->comp.type) {
|
||||
case COUNTER_COMP_FUNCTION:
|
||||
return sysfs_emit(buf, "%s\n", counter_function_str[data]);
|
||||
case COUNTER_COMP_SIGNAL_LEVEL:
|
||||
return sysfs_emit(buf, "%s\n", counter_signal_value_str[data]);
|
||||
case COUNTER_COMP_SYNAPSE_ACTION:
|
||||
return sysfs_emit(buf, "%s\n", counter_synapse_action_str[data]);
|
||||
case COUNTER_COMP_ENUM:
|
||||
return sysfs_emit(buf, "%s\n", avail->strs[data]);
|
||||
case COUNTER_COMP_COUNT_DIRECTION:
|
||||
return sysfs_emit(buf, "%s\n", counter_count_direction_str[data]);
|
||||
case COUNTER_COMP_COUNT_MODE:
|
||||
return sysfs_emit(buf, "%s\n", counter_count_mode_str[data]);
|
||||
default:
|
||||
return sysfs_emit(buf, "%u\n", (unsigned int)data);
|
||||
}
|
||||
}
|
||||
|
||||
static int counter_find_enum(u32 *const enum_item, const u32 *const enums,
|
||||
const size_t num_enums, const char *const buf,
|
||||
const char *const string_array[])
|
||||
{
|
||||
size_t index;
|
||||
|
||||
for (index = 0; index < num_enums; index++) {
|
||||
*enum_item = enums[index];
|
||||
if (sysfs_streq(buf, string_array[*enum_item]))
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static ssize_t counter_comp_u32_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
const struct counter_attribute *const a = to_counter_attribute(attr);
|
||||
struct counter_device *const counter = dev_get_drvdata(dev);
|
||||
struct counter_count *const count = a->parent;
|
||||
struct counter_synapse *const synapse = a->comp.priv;
|
||||
const struct counter_available *const avail = a->comp.priv;
|
||||
int err;
|
||||
u32 data = 0;
|
||||
|
||||
switch (a->comp.type) {
|
||||
case COUNTER_COMP_FUNCTION:
|
||||
err = counter_find_enum(&data, count->functions_list,
|
||||
count->num_functions, buf,
|
||||
counter_function_str);
|
||||
break;
|
||||
case COUNTER_COMP_SYNAPSE_ACTION:
|
||||
err = counter_find_enum(&data, synapse->actions_list,
|
||||
synapse->num_actions, buf,
|
||||
counter_synapse_action_str);
|
||||
break;
|
||||
case COUNTER_COMP_ENUM:
|
||||
err = __sysfs_match_string(avail->strs, avail->num_items, buf);
|
||||
data = err;
|
||||
break;
|
||||
case COUNTER_COMP_COUNT_MODE:
|
||||
err = counter_find_enum(&data, avail->enums, avail->num_items,
|
||||
buf, counter_count_mode_str);
|
||||
break;
|
||||
default:
|
||||
err = kstrtou32(buf, 0, &data);
|
||||
break;
|
||||
}
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
switch (a->scope) {
|
||||
case COUNTER_SCOPE_DEVICE:
|
||||
err = a->comp.device_u32_write(counter, data);
|
||||
break;
|
||||
case COUNTER_SCOPE_SIGNAL:
|
||||
err = a->comp.signal_u32_write(counter, a->parent, data);
|
||||
break;
|
||||
case COUNTER_SCOPE_COUNT:
|
||||
if (a->comp.type == COUNTER_COMP_SYNAPSE_ACTION)
|
||||
err = a->comp.action_write(counter, count, synapse,
|
||||
data);
|
||||
else
|
||||
err = a->comp.count_u32_write(counter, count, data);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t counter_comp_u64_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
const struct counter_attribute *const a = to_counter_attribute(attr);
|
||||
struct counter_device *const counter = dev_get_drvdata(dev);
|
||||
int err;
|
||||
u64 data = 0;
|
||||
|
||||
switch (a->scope) {
|
||||
case COUNTER_SCOPE_DEVICE:
|
||||
err = a->comp.device_u64_read(counter, &data);
|
||||
break;
|
||||
case COUNTER_SCOPE_SIGNAL:
|
||||
err = a->comp.signal_u64_read(counter, a->parent, &data);
|
||||
break;
|
||||
case COUNTER_SCOPE_COUNT:
|
||||
err = a->comp.count_u64_read(counter, a->parent, &data);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return sysfs_emit(buf, "%llu\n", (unsigned long long)data);
|
||||
}
|
||||
|
||||
static ssize_t counter_comp_u64_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
const struct counter_attribute *const a = to_counter_attribute(attr);
|
||||
struct counter_device *const counter = dev_get_drvdata(dev);
|
||||
int err;
|
||||
u64 data = 0;
|
||||
|
||||
err = kstrtou64(buf, 0, &data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
switch (a->scope) {
|
||||
case COUNTER_SCOPE_DEVICE:
|
||||
err = a->comp.device_u64_write(counter, data);
|
||||
break;
|
||||
case COUNTER_SCOPE_SIGNAL:
|
||||
err = a->comp.signal_u64_write(counter, a->parent, data);
|
||||
break;
|
||||
case COUNTER_SCOPE_COUNT:
|
||||
err = a->comp.count_u64_write(counter, a->parent, data);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t enums_available_show(const u32 *const enums,
|
||||
const size_t num_enums,
|
||||
const char *const strs[], char *buf)
|
||||
{
|
||||
size_t len = 0;
|
||||
size_t index;
|
||||
|
||||
for (index = 0; index < num_enums; index++)
|
||||
len += sysfs_emit_at(buf, len, "%s\n", strs[enums[index]]);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t strs_available_show(const struct counter_available *const avail,
|
||||
char *buf)
|
||||
{
|
||||
size_t len = 0;
|
||||
size_t index;
|
||||
|
||||
for (index = 0; index < avail->num_items; index++)
|
||||
len += sysfs_emit_at(buf, len, "%s\n", avail->strs[index]);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t counter_comp_available_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
const struct counter_attribute *const a = to_counter_attribute(attr);
|
||||
const struct counter_count *const count = a->parent;
|
||||
const struct counter_synapse *const synapse = a->comp.priv;
|
||||
const struct counter_available *const avail = a->comp.priv;
|
||||
|
||||
switch (a->comp.type) {
|
||||
case COUNTER_COMP_FUNCTION:
|
||||
return enums_available_show(count->functions_list,
|
||||
count->num_functions,
|
||||
counter_function_str, buf);
|
||||
case COUNTER_COMP_SYNAPSE_ACTION:
|
||||
return enums_available_show(synapse->actions_list,
|
||||
synapse->num_actions,
|
||||
counter_synapse_action_str, buf);
|
||||
case COUNTER_COMP_ENUM:
|
||||
return strs_available_show(avail, buf);
|
||||
case COUNTER_COMP_COUNT_MODE:
|
||||
return enums_available_show(avail->enums, avail->num_items,
|
||||
counter_count_mode_str, buf);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int counter_avail_attr_create(struct device *const dev,
|
||||
struct counter_attribute_group *const group,
|
||||
const struct counter_comp *const comp, void *const parent)
|
||||
{
|
||||
struct counter_attribute *counter_attr;
|
||||
struct device_attribute *dev_attr;
|
||||
|
||||
counter_attr = devm_kzalloc(dev, sizeof(*counter_attr), GFP_KERNEL);
|
||||
if (!counter_attr)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Configure Counter attribute */
|
||||
counter_attr->comp.type = comp->type;
|
||||
counter_attr->comp.priv = comp->priv;
|
||||
counter_attr->parent = parent;
|
||||
|
||||
/* Initialize sysfs attribute */
|
||||
dev_attr = &counter_attr->dev_attr;
|
||||
sysfs_attr_init(&dev_attr->attr);
|
||||
|
||||
/* Configure device attribute */
|
||||
dev_attr->attr.name = devm_kasprintf(dev, GFP_KERNEL, "%s_available",
|
||||
comp->name);
|
||||
if (!dev_attr->attr.name)
|
||||
return -ENOMEM;
|
||||
dev_attr->attr.mode = 0444;
|
||||
dev_attr->show = counter_comp_available_show;
|
||||
|
||||
/* Store list node */
|
||||
list_add(&counter_attr->l, &group->attr_list);
|
||||
group->num_attr++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int counter_attr_create(struct device *const dev,
|
||||
struct counter_attribute_group *const group,
|
||||
const struct counter_comp *const comp,
|
||||
const enum counter_scope scope,
|
||||
void *const parent)
|
||||
{
|
||||
struct counter_attribute *counter_attr;
|
||||
struct device_attribute *dev_attr;
|
||||
|
||||
counter_attr = devm_kzalloc(dev, sizeof(*counter_attr), GFP_KERNEL);
|
||||
if (!counter_attr)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Configure Counter attribute */
|
||||
counter_attr->comp = *comp;
|
||||
counter_attr->scope = scope;
|
||||
counter_attr->parent = parent;
|
||||
|
||||
/* Configure device attribute */
|
||||
dev_attr = &counter_attr->dev_attr;
|
||||
sysfs_attr_init(&dev_attr->attr);
|
||||
dev_attr->attr.name = comp->name;
|
||||
switch (comp->type) {
|
||||
case COUNTER_COMP_U8:
|
||||
case COUNTER_COMP_BOOL:
|
||||
if (comp->device_u8_read) {
|
||||
dev_attr->attr.mode |= 0444;
|
||||
dev_attr->show = counter_comp_u8_show;
|
||||
}
|
||||
if (comp->device_u8_write) {
|
||||
dev_attr->attr.mode |= 0200;
|
||||
dev_attr->store = counter_comp_u8_store;
|
||||
}
|
||||
break;
|
||||
case COUNTER_COMP_SIGNAL_LEVEL:
|
||||
case COUNTER_COMP_FUNCTION:
|
||||
case COUNTER_COMP_SYNAPSE_ACTION:
|
||||
case COUNTER_COMP_ENUM:
|
||||
case COUNTER_COMP_COUNT_DIRECTION:
|
||||
case COUNTER_COMP_COUNT_MODE:
|
||||
if (comp->device_u32_read) {
|
||||
dev_attr->attr.mode |= 0444;
|
||||
dev_attr->show = counter_comp_u32_show;
|
||||
}
|
||||
if (comp->device_u32_write) {
|
||||
dev_attr->attr.mode |= 0200;
|
||||
dev_attr->store = counter_comp_u32_store;
|
||||
}
|
||||
break;
|
||||
case COUNTER_COMP_U64:
|
||||
if (comp->device_u64_read) {
|
||||
dev_attr->attr.mode |= 0444;
|
||||
dev_attr->show = counter_comp_u64_show;
|
||||
}
|
||||
if (comp->device_u64_write) {
|
||||
dev_attr->attr.mode |= 0200;
|
||||
dev_attr->store = counter_comp_u64_store;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Store list node */
|
||||
list_add(&counter_attr->l, &group->attr_list);
|
||||
group->num_attr++;
|
||||
|
||||
/* Create "*_available" attribute if needed */
|
||||
switch (comp->type) {
|
||||
case COUNTER_COMP_FUNCTION:
|
||||
case COUNTER_COMP_SYNAPSE_ACTION:
|
||||
case COUNTER_COMP_ENUM:
|
||||
case COUNTER_COMP_COUNT_MODE:
|
||||
return counter_avail_attr_create(dev, group, comp, parent);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t counter_comp_name_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return sysfs_emit(buf, "%s\n", to_counter_attribute(attr)->comp.name);
|
||||
}
|
||||
|
||||
static int counter_name_attr_create(struct device *const dev,
|
||||
struct counter_attribute_group *const group,
|
||||
const char *const name)
|
||||
{
|
||||
struct counter_attribute *counter_attr;
|
||||
|
||||
counter_attr = devm_kzalloc(dev, sizeof(*counter_attr), GFP_KERNEL);
|
||||
if (!counter_attr)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Configure Counter attribute */
|
||||
counter_attr->comp.name = name;
|
||||
|
||||
/* Configure device attribute */
|
||||
sysfs_attr_init(&counter_attr->dev_attr.attr);
|
||||
counter_attr->dev_attr.attr.name = "name";
|
||||
counter_attr->dev_attr.attr.mode = 0444;
|
||||
counter_attr->dev_attr.show = counter_comp_name_show;
|
||||
|
||||
/* Store list node */
|
||||
list_add(&counter_attr->l, &group->attr_list);
|
||||
group->num_attr++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t counter_comp_id_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
const size_t id = (size_t)to_counter_attribute(attr)->comp.priv;
|
||||
|
||||
return sysfs_emit(buf, "%zu\n", id);
|
||||
}
|
||||
|
||||
static int counter_comp_id_attr_create(struct device *const dev,
|
||||
struct counter_attribute_group *const group,
|
||||
const char *name, const size_t id)
|
||||
{
|
||||
struct counter_attribute *counter_attr;
|
||||
|
||||
/* Allocate Counter attribute */
|
||||
counter_attr = devm_kzalloc(dev, sizeof(*counter_attr), GFP_KERNEL);
|
||||
if (!counter_attr)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Generate component ID name */
|
||||
name = devm_kasprintf(dev, GFP_KERNEL, "%s_component_id", name);
|
||||
if (!name)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Configure Counter attribute */
|
||||
counter_attr->comp.priv = (void *)id;
|
||||
|
||||
/* Configure device attribute */
|
||||
sysfs_attr_init(&counter_attr->dev_attr.attr);
|
||||
counter_attr->dev_attr.attr.name = name;
|
||||
counter_attr->dev_attr.attr.mode = 0444;
|
||||
counter_attr->dev_attr.show = counter_comp_id_show;
|
||||
|
||||
/* Store list node */
|
||||
list_add(&counter_attr->l, &group->attr_list);
|
||||
group->num_attr++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct counter_comp counter_signal_comp = {
|
||||
.type = COUNTER_COMP_SIGNAL_LEVEL,
|
||||
.name = "signal",
|
||||
};
|
||||
|
||||
static int counter_signal_attrs_create(struct counter_device *const counter,
|
||||
struct counter_attribute_group *const cattr_group,
|
||||
struct counter_signal *const signal)
|
||||
{
|
||||
const enum counter_scope scope = COUNTER_SCOPE_SIGNAL;
|
||||
struct device *const dev = &counter->dev;
|
||||
int err;
|
||||
struct counter_comp comp;
|
||||
size_t i;
|
||||
struct counter_comp *ext;
|
||||
|
||||
/* Create main Signal attribute */
|
||||
comp = counter_signal_comp;
|
||||
comp.signal_u32_read = counter->ops->signal_read;
|
||||
err = counter_attr_create(dev, cattr_group, &comp, scope, signal);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Create Signal name attribute */
|
||||
err = counter_name_attr_create(dev, cattr_group, signal->name);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Create an attribute for each extension */
|
||||
for (i = 0; i < signal->num_ext; i++) {
|
||||
ext = &signal->ext[i];
|
||||
|
||||
err = counter_attr_create(dev, cattr_group, ext, scope, signal);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = counter_comp_id_attr_create(dev, cattr_group, ext->name,
|
||||
i);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int counter_sysfs_signals_add(struct counter_device *const counter,
|
||||
struct counter_attribute_group *const groups)
|
||||
{
|
||||
size_t i;
|
||||
int err;
|
||||
|
||||
/* Add each Signal */
|
||||
for (i = 0; i < counter->num_signals; i++) {
|
||||
/* Generate Signal attribute directory name */
|
||||
groups[i].name = devm_kasprintf(&counter->dev, GFP_KERNEL,
|
||||
"signal%zu", i);
|
||||
if (!groups[i].name)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Create all attributes associated with Signal */
|
||||
err = counter_signal_attrs_create(counter, groups + i,
|
||||
counter->signals + i);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int counter_sysfs_synapses_add(struct counter_device *const counter,
|
||||
struct counter_attribute_group *const group,
|
||||
struct counter_count *const count)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
/* Add each Synapse */
|
||||
for (i = 0; i < count->num_synapses; i++) {
|
||||
struct device *const dev = &counter->dev;
|
||||
struct counter_synapse *synapse;
|
||||
size_t id;
|
||||
struct counter_comp comp;
|
||||
int err;
|
||||
|
||||
synapse = count->synapses + i;
|
||||
|
||||
/* Generate Synapse action name */
|
||||
id = synapse->signal - counter->signals;
|
||||
comp.name = devm_kasprintf(dev, GFP_KERNEL, "signal%zu_action",
|
||||
id);
|
||||
if (!comp.name)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Create action attribute */
|
||||
comp.type = COUNTER_COMP_SYNAPSE_ACTION;
|
||||
comp.action_read = counter->ops->action_read;
|
||||
comp.action_write = counter->ops->action_write;
|
||||
comp.priv = synapse;
|
||||
err = counter_attr_create(dev, group, &comp,
|
||||
COUNTER_SCOPE_COUNT, count);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Create Synapse component ID attribute */
|
||||
err = counter_comp_id_attr_create(dev, group, comp.name, i);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct counter_comp counter_count_comp =
|
||||
COUNTER_COMP_COUNT_U64("count", NULL, NULL);
|
||||
|
||||
static struct counter_comp counter_function_comp = {
|
||||
.type = COUNTER_COMP_FUNCTION,
|
||||
.name = "function",
|
||||
};
|
||||
|
||||
static int counter_count_attrs_create(struct counter_device *const counter,
|
||||
struct counter_attribute_group *const cattr_group,
|
||||
struct counter_count *const count)
|
||||
{
|
||||
const enum counter_scope scope = COUNTER_SCOPE_COUNT;
|
||||
struct device *const dev = &counter->dev;
|
||||
int err;
|
||||
struct counter_comp comp;
|
||||
size_t i;
|
||||
struct counter_comp *ext;
|
||||
|
||||
/* Create main Count attribute */
|
||||
comp = counter_count_comp;
|
||||
comp.count_u64_read = counter->ops->count_read;
|
||||
comp.count_u64_write = counter->ops->count_write;
|
||||
err = counter_attr_create(dev, cattr_group, &comp, scope, count);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Create Count name attribute */
|
||||
err = counter_name_attr_create(dev, cattr_group, count->name);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Create Count function attribute */
|
||||
comp = counter_function_comp;
|
||||
comp.count_u32_read = counter->ops->function_read;
|
||||
comp.count_u32_write = counter->ops->function_write;
|
||||
err = counter_attr_create(dev, cattr_group, &comp, scope, count);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Create an attribute for each extension */
|
||||
for (i = 0; i < count->num_ext; i++) {
|
||||
ext = &count->ext[i];
|
||||
|
||||
err = counter_attr_create(dev, cattr_group, ext, scope, count);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = counter_comp_id_attr_create(dev, cattr_group, ext->name,
|
||||
i);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int counter_sysfs_counts_add(struct counter_device *const counter,
|
||||
struct counter_attribute_group *const groups)
|
||||
{
|
||||
size_t i;
|
||||
struct counter_count *count;
|
||||
int err;
|
||||
|
||||
/* Add each Count */
|
||||
for (i = 0; i < counter->num_counts; i++) {
|
||||
count = counter->counts + i;
|
||||
|
||||
/* Generate Count attribute directory name */
|
||||
groups[i].name = devm_kasprintf(&counter->dev, GFP_KERNEL,
|
||||
"count%zu", i);
|
||||
if (!groups[i].name)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Add sysfs attributes of the Synapses */
|
||||
err = counter_sysfs_synapses_add(counter, groups + i, count);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Create all attributes associated with Count */
|
||||
err = counter_count_attrs_create(counter, groups + i, count);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int counter_num_signals_read(struct counter_device *counter, u8 *val)
|
||||
{
|
||||
*val = counter->num_signals;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int counter_num_counts_read(struct counter_device *counter, u8 *val)
|
||||
{
|
||||
*val = counter->num_counts;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int counter_events_queue_size_read(struct counter_device *counter,
|
||||
u64 *val)
|
||||
{
|
||||
*val = kfifo_size(&counter->events);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int counter_events_queue_size_write(struct counter_device *counter,
|
||||
u64 val)
|
||||
{
|
||||
DECLARE_KFIFO_PTR(events, struct counter_event);
|
||||
int err;
|
||||
unsigned long flags;
|
||||
|
||||
/* Allocate new events queue */
|
||||
err = kfifo_alloc(&events, val, GFP_KERNEL);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Swap in new events queue */
|
||||
mutex_lock(&counter->events_out_lock);
|
||||
spin_lock_irqsave(&counter->events_in_lock, flags);
|
||||
kfifo_free(&counter->events);
|
||||
counter->events.kfifo = events.kfifo;
|
||||
spin_unlock_irqrestore(&counter->events_in_lock, flags);
|
||||
mutex_unlock(&counter->events_out_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct counter_comp counter_num_signals_comp =
|
||||
COUNTER_COMP_DEVICE_U8("num_signals", counter_num_signals_read, NULL);
|
||||
|
||||
static struct counter_comp counter_num_counts_comp =
|
||||
COUNTER_COMP_DEVICE_U8("num_counts", counter_num_counts_read, NULL);
|
||||
|
||||
static struct counter_comp counter_events_queue_size_comp =
|
||||
COUNTER_COMP_DEVICE_U64("events_queue_size",
|
||||
counter_events_queue_size_read,
|
||||
counter_events_queue_size_write);
|
||||
|
||||
static int counter_sysfs_attr_add(struct counter_device *const counter,
|
||||
struct counter_attribute_group *cattr_group)
|
||||
{
|
||||
const enum counter_scope scope = COUNTER_SCOPE_DEVICE;
|
||||
struct device *const dev = &counter->dev;
|
||||
int err;
|
||||
size_t i;
|
||||
struct counter_comp *ext;
|
||||
|
||||
/* Add Signals sysfs attributes */
|
||||
err = counter_sysfs_signals_add(counter, cattr_group);
|
||||
if (err < 0)
|
||||
return err;
|
||||
cattr_group += counter->num_signals;
|
||||
|
||||
/* Add Counts sysfs attributes */
|
||||
err = counter_sysfs_counts_add(counter, cattr_group);
|
||||
if (err < 0)
|
||||
return err;
|
||||
cattr_group += counter->num_counts;
|
||||
|
||||
/* Create name attribute */
|
||||
err = counter_name_attr_create(dev, cattr_group, counter->name);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Create num_signals attribute */
|
||||
err = counter_attr_create(dev, cattr_group, &counter_num_signals_comp,
|
||||
scope, NULL);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Create num_counts attribute */
|
||||
err = counter_attr_create(dev, cattr_group, &counter_num_counts_comp,
|
||||
scope, NULL);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Create events_queue_size attribute */
|
||||
err = counter_attr_create(dev, cattr_group,
|
||||
&counter_events_queue_size_comp, scope, NULL);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Create an attribute for each extension */
|
||||
for (i = 0; i < counter->num_ext; i++) {
|
||||
ext = &counter->ext[i];
|
||||
|
||||
err = counter_attr_create(dev, cattr_group, ext, scope, NULL);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = counter_comp_id_attr_create(dev, cattr_group, ext->name,
|
||||
i);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* counter_sysfs_add - Adds Counter sysfs attributes to the device structure
|
||||
* @counter: Pointer to the Counter device structure
|
||||
*
|
||||
* Counter sysfs attributes are created and added to the respective device
|
||||
* structure for later registration to the system. Resource-managed memory
|
||||
* allocation is performed by this function, and this memory should be freed
|
||||
* when no longer needed (automatically by a device_unregister call, or
|
||||
* manually by a devres_release_all call).
|
||||
*/
|
||||
int counter_sysfs_add(struct counter_device *const counter)
|
||||
{
|
||||
struct device *const dev = &counter->dev;
|
||||
const size_t num_groups = counter->num_signals + counter->num_counts + 1;
|
||||
struct counter_attribute_group *cattr_groups;
|
||||
size_t i, j;
|
||||
int err;
|
||||
struct attribute_group *groups;
|
||||
struct counter_attribute *p;
|
||||
|
||||
/* Allocate space for attribute groups (signals, counts, and ext) */
|
||||
cattr_groups = devm_kcalloc(dev, num_groups, sizeof(*cattr_groups),
|
||||
GFP_KERNEL);
|
||||
if (!cattr_groups)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Initialize attribute lists */
|
||||
for (i = 0; i < num_groups; i++)
|
||||
INIT_LIST_HEAD(&cattr_groups[i].attr_list);
|
||||
|
||||
/* Add Counter device sysfs attributes */
|
||||
err = counter_sysfs_attr_add(counter, cattr_groups);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Allocate attribute group pointers for association with device */
|
||||
dev->groups = devm_kcalloc(dev, num_groups + 1, sizeof(*dev->groups),
|
||||
GFP_KERNEL);
|
||||
if (!dev->groups)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Allocate space for attribute groups */
|
||||
groups = devm_kcalloc(dev, num_groups, sizeof(*groups), GFP_KERNEL);
|
||||
if (!groups)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Prepare each group of attributes for association */
|
||||
for (i = 0; i < num_groups; i++) {
|
||||
groups[i].name = cattr_groups[i].name;
|
||||
|
||||
/* Allocate space for attribute pointers */
|
||||
groups[i].attrs = devm_kcalloc(dev,
|
||||
cattr_groups[i].num_attr + 1,
|
||||
sizeof(*groups[i].attrs),
|
||||
GFP_KERNEL);
|
||||
if (!groups[i].attrs)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Add attribute pointers to attribute group */
|
||||
j = 0;
|
||||
list_for_each_entry(p, &cattr_groups[i].attr_list, l)
|
||||
groups[i].attrs[j++] = &p->dev_attr.attr;
|
||||
|
||||
/* Associate attribute group */
|
||||
dev->groups[i] = &groups[i];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Counter sysfs interface
|
||||
* Copyright (C) 2020 William Breathitt Gray
|
||||
*/
|
||||
#ifndef _COUNTER_SYSFS_H_
|
||||
#define _COUNTER_SYSFS_H_
|
||||
|
||||
#include <linux/counter.h>
|
||||
|
||||
int counter_sysfs_add(struct counter_device *const counter);
|
||||
|
||||
#endif /* _COUNTER_SYSFS_H_ */
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -14,6 +14,7 @@
|
|||
#include <linux/mutex.h>
|
||||
#include <linux/counter.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#define FTM_FIELD_UPDATE(ftm, offset, mask, val) \
|
||||
({ \
|
||||
|
@ -115,8 +116,7 @@ static void ftm_quaddec_disable(void *ftm)
|
|||
}
|
||||
|
||||
static int ftm_quaddec_get_prescaler(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
size_t *cnt_mode)
|
||||
struct counter_count *count, u32 *cnt_mode)
|
||||
{
|
||||
struct ftm_quaddec *ftm = counter->priv;
|
||||
uint32_t scflags;
|
||||
|
@ -129,8 +129,7 @@ static int ftm_quaddec_get_prescaler(struct counter_device *counter,
|
|||
}
|
||||
|
||||
static int ftm_quaddec_set_prescaler(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
size_t cnt_mode)
|
||||
struct counter_count *count, u32 cnt_mode)
|
||||
{
|
||||
struct ftm_quaddec *ftm = counter->priv;
|
||||
|
||||
|
@ -151,33 +150,17 @@ static const char * const ftm_quaddec_prescaler[] = {
|
|||
"1", "2", "4", "8", "16", "32", "64", "128"
|
||||
};
|
||||
|
||||
static struct counter_count_enum_ext ftm_quaddec_prescaler_enum = {
|
||||
.items = ftm_quaddec_prescaler,
|
||||
.num_items = ARRAY_SIZE(ftm_quaddec_prescaler),
|
||||
.get = ftm_quaddec_get_prescaler,
|
||||
.set = ftm_quaddec_set_prescaler
|
||||
};
|
||||
|
||||
enum ftm_quaddec_synapse_action {
|
||||
FTM_QUADDEC_SYNAPSE_ACTION_BOTH_EDGES,
|
||||
};
|
||||
|
||||
static const enum counter_synapse_action ftm_quaddec_synapse_actions[] = {
|
||||
[FTM_QUADDEC_SYNAPSE_ACTION_BOTH_EDGES] =
|
||||
COUNTER_SYNAPSE_ACTION_BOTH_EDGES
|
||||
};
|
||||
|
||||
enum ftm_quaddec_count_function {
|
||||
FTM_QUADDEC_COUNT_ENCODER_MODE_1,
|
||||
};
|
||||
|
||||
static const enum counter_function ftm_quaddec_count_functions[] = {
|
||||
[FTM_QUADDEC_COUNT_ENCODER_MODE_1] = COUNTER_FUNCTION_QUADRATURE_X4
|
||||
COUNTER_FUNCTION_QUADRATURE_X4
|
||||
};
|
||||
|
||||
static int ftm_quaddec_count_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
unsigned long *val)
|
||||
u64 *val)
|
||||
{
|
||||
struct ftm_quaddec *const ftm = counter->priv;
|
||||
uint32_t cntval;
|
||||
|
@ -191,7 +174,7 @@ static int ftm_quaddec_count_read(struct counter_device *counter,
|
|||
|
||||
static int ftm_quaddec_count_write(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
const unsigned long val)
|
||||
const u64 val)
|
||||
{
|
||||
struct ftm_quaddec *const ftm = counter->priv;
|
||||
|
||||
|
@ -205,21 +188,21 @@ static int ftm_quaddec_count_write(struct counter_device *counter,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ftm_quaddec_count_function_get(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
size_t *function)
|
||||
static int ftm_quaddec_count_function_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
enum counter_function *function)
|
||||
{
|
||||
*function = FTM_QUADDEC_COUNT_ENCODER_MODE_1;
|
||||
*function = COUNTER_FUNCTION_QUADRATURE_X4;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ftm_quaddec_action_get(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
struct counter_synapse *synapse,
|
||||
size_t *action)
|
||||
static int ftm_quaddec_action_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
struct counter_synapse *synapse,
|
||||
enum counter_synapse_action *action)
|
||||
{
|
||||
*action = FTM_QUADDEC_SYNAPSE_ACTION_BOTH_EDGES;
|
||||
*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -227,8 +210,8 @@ static int ftm_quaddec_action_get(struct counter_device *counter,
|
|||
static const struct counter_ops ftm_quaddec_cnt_ops = {
|
||||
.count_read = ftm_quaddec_count_read,
|
||||
.count_write = ftm_quaddec_count_write,
|
||||
.function_get = ftm_quaddec_count_function_get,
|
||||
.action_get = ftm_quaddec_action_get,
|
||||
.function_read = ftm_quaddec_count_function_read,
|
||||
.action_read = ftm_quaddec_action_read,
|
||||
};
|
||||
|
||||
static struct counter_signal ftm_quaddec_signals[] = {
|
||||
|
@ -255,9 +238,12 @@ static struct counter_synapse ftm_quaddec_count_synapses[] = {
|
|||
}
|
||||
};
|
||||
|
||||
static const struct counter_count_ext ftm_quaddec_count_ext[] = {
|
||||
COUNTER_COUNT_ENUM("prescaler", &ftm_quaddec_prescaler_enum),
|
||||
COUNTER_COUNT_ENUM_AVAILABLE("prescaler", &ftm_quaddec_prescaler_enum),
|
||||
static DEFINE_COUNTER_ENUM(ftm_quaddec_prescaler_enum, ftm_quaddec_prescaler);
|
||||
|
||||
static struct counter_comp ftm_quaddec_count_ext[] = {
|
||||
COUNTER_COMP_COUNT_ENUM("prescaler", ftm_quaddec_get_prescaler,
|
||||
ftm_quaddec_set_prescaler,
|
||||
ftm_quaddec_prescaler_enum),
|
||||
};
|
||||
|
||||
static struct counter_count ftm_quaddec_counts = {
|
||||
|
|
|
@ -62,13 +62,6 @@
|
|||
|
||||
#define INTEL_QEP_CLK_PERIOD_NS 10
|
||||
|
||||
#define INTEL_QEP_COUNTER_EXT_RW(_name) \
|
||||
{ \
|
||||
.name = #_name, \
|
||||
.read = _name##_read, \
|
||||
.write = _name##_write, \
|
||||
}
|
||||
|
||||
struct intel_qep {
|
||||
struct counter_device counter;
|
||||
struct mutex lock;
|
||||
|
@ -114,8 +107,7 @@ static void intel_qep_init(struct intel_qep *qep)
|
|||
}
|
||||
|
||||
static int intel_qep_count_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
unsigned long *val)
|
||||
struct counter_count *count, u64 *val)
|
||||
{
|
||||
struct intel_qep *const qep = counter->priv;
|
||||
|
||||
|
@ -130,11 +122,11 @@ static const enum counter_function intel_qep_count_functions[] = {
|
|||
COUNTER_FUNCTION_QUADRATURE_X4,
|
||||
};
|
||||
|
||||
static int intel_qep_function_get(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
size_t *function)
|
||||
static int intel_qep_function_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
enum counter_function *function)
|
||||
{
|
||||
*function = 0;
|
||||
*function = COUNTER_FUNCTION_QUADRATURE_X4;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -143,19 +135,19 @@ static const enum counter_synapse_action intel_qep_synapse_actions[] = {
|
|||
COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
|
||||
};
|
||||
|
||||
static int intel_qep_action_get(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
struct counter_synapse *synapse,
|
||||
size_t *action)
|
||||
static int intel_qep_action_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
struct counter_synapse *synapse,
|
||||
enum counter_synapse_action *action)
|
||||
{
|
||||
*action = 0;
|
||||
*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct counter_ops intel_qep_counter_ops = {
|
||||
.count_read = intel_qep_count_read,
|
||||
.function_get = intel_qep_function_get,
|
||||
.action_get = intel_qep_action_get,
|
||||
.function_read = intel_qep_function_read,
|
||||
.action_read = intel_qep_action_read,
|
||||
};
|
||||
|
||||
#define INTEL_QEP_SIGNAL(_id, _name) { \
|
||||
|
@ -181,31 +173,27 @@ static struct counter_synapse intel_qep_count_synapses[] = {
|
|||
INTEL_QEP_SYNAPSE(2),
|
||||
};
|
||||
|
||||
static ssize_t ceiling_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
void *priv, char *buf)
|
||||
static int intel_qep_ceiling_read(struct counter_device *counter,
|
||||
struct counter_count *count, u64 *ceiling)
|
||||
{
|
||||
struct intel_qep *qep = counter->priv;
|
||||
u32 reg;
|
||||
|
||||
pm_runtime_get_sync(qep->dev);
|
||||
reg = intel_qep_readl(qep, INTEL_QEPMAX);
|
||||
*ceiling = intel_qep_readl(qep, INTEL_QEPMAX);
|
||||
pm_runtime_put(qep->dev);
|
||||
|
||||
return sysfs_emit(buf, "%u\n", reg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t ceiling_write(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
void *priv, const char *buf, size_t len)
|
||||
static int intel_qep_ceiling_write(struct counter_device *counter,
|
||||
struct counter_count *count, u64 max)
|
||||
{
|
||||
struct intel_qep *qep = counter->priv;
|
||||
u32 max;
|
||||
int ret;
|
||||
int ret = 0;
|
||||
|
||||
ret = kstrtou32(buf, 0, &max);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
/* Intel QEP ceiling configuration only supports 32-bit values */
|
||||
if (max != (u32)max)
|
||||
return -ERANGE;
|
||||
|
||||
mutex_lock(&qep->lock);
|
||||
if (qep->enabled) {
|
||||
|
@ -216,34 +204,28 @@ static ssize_t ceiling_write(struct counter_device *counter,
|
|||
pm_runtime_get_sync(qep->dev);
|
||||
intel_qep_writel(qep, INTEL_QEPMAX, max);
|
||||
pm_runtime_put(qep->dev);
|
||||
ret = len;
|
||||
|
||||
out:
|
||||
mutex_unlock(&qep->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t enable_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
void *priv, char *buf)
|
||||
static int intel_qep_enable_read(struct counter_device *counter,
|
||||
struct counter_count *count, u8 *enable)
|
||||
{
|
||||
struct intel_qep *qep = counter->priv;
|
||||
|
||||
return sysfs_emit(buf, "%u\n", qep->enabled);
|
||||
*enable = qep->enabled;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t enable_write(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
void *priv, const char *buf, size_t len)
|
||||
static int intel_qep_enable_write(struct counter_device *counter,
|
||||
struct counter_count *count, u8 val)
|
||||
{
|
||||
struct intel_qep *qep = counter->priv;
|
||||
u32 reg;
|
||||
bool val, changed;
|
||||
int ret;
|
||||
|
||||
ret = kstrtobool(buf, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
bool changed;
|
||||
|
||||
mutex_lock(&qep->lock);
|
||||
changed = val ^ qep->enabled;
|
||||
|
@ -267,12 +249,12 @@ static ssize_t enable_write(struct counter_device *counter,
|
|||
|
||||
out:
|
||||
mutex_unlock(&qep->lock);
|
||||
return len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t spike_filter_ns_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
void *priv, char *buf)
|
||||
static int intel_qep_spike_filter_ns_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
u64 *length)
|
||||
{
|
||||
struct intel_qep *qep = counter->priv;
|
||||
u32 reg;
|
||||
|
@ -281,33 +263,31 @@ static ssize_t spike_filter_ns_read(struct counter_device *counter,
|
|||
reg = intel_qep_readl(qep, INTEL_QEPCON);
|
||||
if (!(reg & INTEL_QEPCON_FLT_EN)) {
|
||||
pm_runtime_put(qep->dev);
|
||||
return sysfs_emit(buf, "0\n");
|
||||
return 0;
|
||||
}
|
||||
reg = INTEL_QEPFLT_MAX_COUNT(intel_qep_readl(qep, INTEL_QEPFLT));
|
||||
pm_runtime_put(qep->dev);
|
||||
|
||||
return sysfs_emit(buf, "%u\n", (reg + 2) * INTEL_QEP_CLK_PERIOD_NS);
|
||||
*length = (reg + 2) * INTEL_QEP_CLK_PERIOD_NS;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t spike_filter_ns_write(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
void *priv, const char *buf, size_t len)
|
||||
static int intel_qep_spike_filter_ns_write(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
u64 length)
|
||||
{
|
||||
struct intel_qep *qep = counter->priv;
|
||||
u32 reg, length;
|
||||
u32 reg;
|
||||
bool enable;
|
||||
int ret;
|
||||
|
||||
ret = kstrtou32(buf, 0, &length);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
int ret = 0;
|
||||
|
||||
/*
|
||||
* Spike filter length is (MAX_COUNT + 2) clock periods.
|
||||
* Disable filter when userspace writes 0, enable for valid
|
||||
* nanoseconds values and error out otherwise.
|
||||
*/
|
||||
length /= INTEL_QEP_CLK_PERIOD_NS;
|
||||
do_div(length, INTEL_QEP_CLK_PERIOD_NS);
|
||||
if (length == 0) {
|
||||
enable = false;
|
||||
length = 0;
|
||||
|
@ -336,16 +316,15 @@ static ssize_t spike_filter_ns_write(struct counter_device *counter,
|
|||
intel_qep_writel(qep, INTEL_QEPFLT, length);
|
||||
intel_qep_writel(qep, INTEL_QEPCON, reg);
|
||||
pm_runtime_put(qep->dev);
|
||||
ret = len;
|
||||
|
||||
out:
|
||||
mutex_unlock(&qep->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t preset_enable_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
void *priv, char *buf)
|
||||
static int intel_qep_preset_enable_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
u8 *preset_enable)
|
||||
{
|
||||
struct intel_qep *qep = counter->priv;
|
||||
u32 reg;
|
||||
|
@ -353,21 +332,18 @@ static ssize_t preset_enable_read(struct counter_device *counter,
|
|||
pm_runtime_get_sync(qep->dev);
|
||||
reg = intel_qep_readl(qep, INTEL_QEPCON);
|
||||
pm_runtime_put(qep->dev);
|
||||
return sysfs_emit(buf, "%u\n", !(reg & INTEL_QEPCON_COUNT_RST_MODE));
|
||||
|
||||
*preset_enable = !(reg & INTEL_QEPCON_COUNT_RST_MODE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t preset_enable_write(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
void *priv, const char *buf, size_t len)
|
||||
static int intel_qep_preset_enable_write(struct counter_device *counter,
|
||||
struct counter_count *count, u8 val)
|
||||
{
|
||||
struct intel_qep *qep = counter->priv;
|
||||
u32 reg;
|
||||
bool val;
|
||||
int ret;
|
||||
|
||||
ret = kstrtobool(buf, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&qep->lock);
|
||||
if (qep->enabled) {
|
||||
|
@ -384,7 +360,6 @@ static ssize_t preset_enable_write(struct counter_device *counter,
|
|||
|
||||
intel_qep_writel(qep, INTEL_QEPCON, reg);
|
||||
pm_runtime_put(qep->dev);
|
||||
ret = len;
|
||||
|
||||
out:
|
||||
mutex_unlock(&qep->lock);
|
||||
|
@ -392,11 +367,14 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static const struct counter_count_ext intel_qep_count_ext[] = {
|
||||
INTEL_QEP_COUNTER_EXT_RW(ceiling),
|
||||
INTEL_QEP_COUNTER_EXT_RW(enable),
|
||||
INTEL_QEP_COUNTER_EXT_RW(spike_filter_ns),
|
||||
INTEL_QEP_COUNTER_EXT_RW(preset_enable)
|
||||
static struct counter_comp intel_qep_count_ext[] = {
|
||||
COUNTER_COMP_ENABLE(intel_qep_enable_read, intel_qep_enable_write),
|
||||
COUNTER_COMP_CEILING(intel_qep_ceiling_read, intel_qep_ceiling_write),
|
||||
COUNTER_COMP_PRESET_ENABLE(intel_qep_preset_enable_read,
|
||||
intel_qep_preset_enable_write),
|
||||
COUNTER_COMP_COUNT_U64("spike_filter_ns",
|
||||
intel_qep_spike_filter_ns_read,
|
||||
intel_qep_spike_filter_ns_write),
|
||||
};
|
||||
|
||||
static struct counter_count intel_qep_counter_count[] = {
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#define INTERRUPT_CNT_NAME "interrupt-cnt"
|
||||
|
||||
|
@ -33,30 +34,23 @@ static irqreturn_t interrupt_cnt_isr(int irq, void *dev_id)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static ssize_t interrupt_cnt_enable_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
void *private, char *buf)
|
||||
static int interrupt_cnt_enable_read(struct counter_device *counter,
|
||||
struct counter_count *count, u8 *enable)
|
||||
{
|
||||
struct interrupt_cnt_priv *priv = counter->priv;
|
||||
|
||||
return sysfs_emit(buf, "%d\n", priv->enabled);
|
||||
*enable = priv->enabled;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t interrupt_cnt_enable_write(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
void *private, const char *buf,
|
||||
size_t len)
|
||||
static int interrupt_cnt_enable_write(struct counter_device *counter,
|
||||
struct counter_count *count, u8 enable)
|
||||
{
|
||||
struct interrupt_cnt_priv *priv = counter->priv;
|
||||
bool enable;
|
||||
ssize_t ret;
|
||||
|
||||
ret = kstrtobool(buf, &enable);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (priv->enabled == enable)
|
||||
return len;
|
||||
return 0;
|
||||
|
||||
if (enable) {
|
||||
priv->enabled = true;
|
||||
|
@ -66,33 +60,30 @@ static ssize_t interrupt_cnt_enable_write(struct counter_device *counter,
|
|||
priv->enabled = false;
|
||||
}
|
||||
|
||||
return len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct counter_count_ext interrupt_cnt_ext[] = {
|
||||
{
|
||||
.name = "enable",
|
||||
.read = interrupt_cnt_enable_read,
|
||||
.write = interrupt_cnt_enable_write,
|
||||
},
|
||||
static struct counter_comp interrupt_cnt_ext[] = {
|
||||
COUNTER_COMP_ENABLE(interrupt_cnt_enable_read,
|
||||
interrupt_cnt_enable_write),
|
||||
};
|
||||
|
||||
static const enum counter_synapse_action interrupt_cnt_synapse_actions[] = {
|
||||
COUNTER_SYNAPSE_ACTION_RISING_EDGE,
|
||||
};
|
||||
|
||||
static int interrupt_cnt_action_get(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
struct counter_synapse *synapse,
|
||||
size_t *action)
|
||||
static int interrupt_cnt_action_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
struct counter_synapse *synapse,
|
||||
enum counter_synapse_action *action)
|
||||
{
|
||||
*action = 0;
|
||||
*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int interrupt_cnt_read(struct counter_device *counter,
|
||||
struct counter_count *count, unsigned long *val)
|
||||
struct counter_count *count, u64 *val)
|
||||
{
|
||||
struct interrupt_cnt_priv *priv = counter->priv;
|
||||
|
||||
|
@ -102,8 +93,7 @@ static int interrupt_cnt_read(struct counter_device *counter,
|
|||
}
|
||||
|
||||
static int interrupt_cnt_write(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
const unsigned long val)
|
||||
struct counter_count *count, const u64 val)
|
||||
{
|
||||
struct interrupt_cnt_priv *priv = counter->priv;
|
||||
|
||||
|
@ -119,11 +109,11 @@ static const enum counter_function interrupt_cnt_functions[] = {
|
|||
COUNTER_FUNCTION_INCREASE,
|
||||
};
|
||||
|
||||
static int interrupt_cnt_function_get(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
size_t *function)
|
||||
static int interrupt_cnt_function_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
enum counter_function *function)
|
||||
{
|
||||
*function = 0;
|
||||
*function = COUNTER_FUNCTION_INCREASE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -148,10 +138,10 @@ static int interrupt_cnt_signal_read(struct counter_device *counter,
|
|||
}
|
||||
|
||||
static const struct counter_ops interrupt_cnt_ops = {
|
||||
.action_get = interrupt_cnt_action_get,
|
||||
.action_read = interrupt_cnt_action_read,
|
||||
.count_read = interrupt_cnt_read,
|
||||
.count_write = interrupt_cnt_write,
|
||||
.function_get = interrupt_cnt_function_get,
|
||||
.function_read = interrupt_cnt_function_read,
|
||||
.signal_read = interrupt_cnt_signal_read,
|
||||
};
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/**
|
||||
/*
|
||||
* Copyright (C) 2020 Microchip
|
||||
*
|
||||
* Author: Kamel Bouhara <kamel.bouhara@bootlin.com>
|
||||
|
@ -32,28 +32,16 @@ struct mchp_tc_data {
|
|||
bool trig_inverted;
|
||||
};
|
||||
|
||||
enum mchp_tc_count_function {
|
||||
MCHP_TC_FUNCTION_INCREASE,
|
||||
MCHP_TC_FUNCTION_QUADRATURE,
|
||||
};
|
||||
|
||||
static const enum counter_function mchp_tc_count_functions[] = {
|
||||
[MCHP_TC_FUNCTION_INCREASE] = COUNTER_FUNCTION_INCREASE,
|
||||
[MCHP_TC_FUNCTION_QUADRATURE] = COUNTER_FUNCTION_QUADRATURE_X4,
|
||||
};
|
||||
|
||||
enum mchp_tc_synapse_action {
|
||||
MCHP_TC_SYNAPSE_ACTION_NONE = 0,
|
||||
MCHP_TC_SYNAPSE_ACTION_RISING_EDGE,
|
||||
MCHP_TC_SYNAPSE_ACTION_FALLING_EDGE,
|
||||
MCHP_TC_SYNAPSE_ACTION_BOTH_EDGE
|
||||
COUNTER_FUNCTION_INCREASE,
|
||||
COUNTER_FUNCTION_QUADRATURE_X4,
|
||||
};
|
||||
|
||||
static const enum counter_synapse_action mchp_tc_synapse_actions[] = {
|
||||
[MCHP_TC_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE,
|
||||
[MCHP_TC_SYNAPSE_ACTION_RISING_EDGE] = COUNTER_SYNAPSE_ACTION_RISING_EDGE,
|
||||
[MCHP_TC_SYNAPSE_ACTION_FALLING_EDGE] = COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
|
||||
[MCHP_TC_SYNAPSE_ACTION_BOTH_EDGE] = COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
|
||||
COUNTER_SYNAPSE_ACTION_NONE,
|
||||
COUNTER_SYNAPSE_ACTION_RISING_EDGE,
|
||||
COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
|
||||
COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
|
||||
};
|
||||
|
||||
static struct counter_signal mchp_tc_count_signals[] = {
|
||||
|
@ -80,23 +68,23 @@ static struct counter_synapse mchp_tc_count_synapses[] = {
|
|||
}
|
||||
};
|
||||
|
||||
static int mchp_tc_count_function_get(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
size_t *function)
|
||||
static int mchp_tc_count_function_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
enum counter_function *function)
|
||||
{
|
||||
struct mchp_tc_data *const priv = counter->priv;
|
||||
|
||||
if (priv->qdec_mode)
|
||||
*function = MCHP_TC_FUNCTION_QUADRATURE;
|
||||
*function = COUNTER_FUNCTION_QUADRATURE_X4;
|
||||
else
|
||||
*function = MCHP_TC_FUNCTION_INCREASE;
|
||||
*function = COUNTER_FUNCTION_INCREASE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mchp_tc_count_function_set(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
size_t function)
|
||||
static int mchp_tc_count_function_write(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
enum counter_function function)
|
||||
{
|
||||
struct mchp_tc_data *const priv = counter->priv;
|
||||
u32 bmr, cmr;
|
||||
|
@ -108,7 +96,7 @@ static int mchp_tc_count_function_set(struct counter_device *counter,
|
|||
cmr &= ~ATMEL_TC_WAVE;
|
||||
|
||||
switch (function) {
|
||||
case MCHP_TC_FUNCTION_INCREASE:
|
||||
case COUNTER_FUNCTION_INCREASE:
|
||||
priv->qdec_mode = 0;
|
||||
/* Set highest rate based on whether soc has gclk or not */
|
||||
bmr &= ~(ATMEL_TC_QDEN | ATMEL_TC_POSEN);
|
||||
|
@ -120,7 +108,7 @@ static int mchp_tc_count_function_set(struct counter_device *counter,
|
|||
cmr |= ATMEL_TC_CMR_MASK;
|
||||
cmr &= ~(ATMEL_TC_ABETRG | ATMEL_TC_XC0);
|
||||
break;
|
||||
case MCHP_TC_FUNCTION_QUADRATURE:
|
||||
case COUNTER_FUNCTION_QUADRATURE_X4:
|
||||
if (!priv->tc_cfg->has_qdec)
|
||||
return -EINVAL;
|
||||
/* In QDEC mode settings both channels 0 and 1 are required */
|
||||
|
@ -176,10 +164,10 @@ static int mchp_tc_count_signal_read(struct counter_device *counter,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int mchp_tc_count_action_get(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
struct counter_synapse *synapse,
|
||||
size_t *action)
|
||||
static int mchp_tc_count_action_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
struct counter_synapse *synapse,
|
||||
enum counter_synapse_action *action)
|
||||
{
|
||||
struct mchp_tc_data *const priv = counter->priv;
|
||||
u32 cmr;
|
||||
|
@ -188,26 +176,26 @@ static int mchp_tc_count_action_get(struct counter_device *counter,
|
|||
|
||||
switch (cmr & ATMEL_TC_ETRGEDG) {
|
||||
default:
|
||||
*action = MCHP_TC_SYNAPSE_ACTION_NONE;
|
||||
*action = COUNTER_SYNAPSE_ACTION_NONE;
|
||||
break;
|
||||
case ATMEL_TC_ETRGEDG_RISING:
|
||||
*action = MCHP_TC_SYNAPSE_ACTION_RISING_EDGE;
|
||||
*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
|
||||
break;
|
||||
case ATMEL_TC_ETRGEDG_FALLING:
|
||||
*action = MCHP_TC_SYNAPSE_ACTION_FALLING_EDGE;
|
||||
*action = COUNTER_SYNAPSE_ACTION_FALLING_EDGE;
|
||||
break;
|
||||
case ATMEL_TC_ETRGEDG_BOTH:
|
||||
*action = MCHP_TC_SYNAPSE_ACTION_BOTH_EDGE;
|
||||
*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mchp_tc_count_action_set(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
struct counter_synapse *synapse,
|
||||
size_t action)
|
||||
static int mchp_tc_count_action_write(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
struct counter_synapse *synapse,
|
||||
enum counter_synapse_action action)
|
||||
{
|
||||
struct mchp_tc_data *const priv = counter->priv;
|
||||
u32 edge = ATMEL_TC_ETRGEDG_NONE;
|
||||
|
@ -217,16 +205,16 @@ static int mchp_tc_count_action_set(struct counter_device *counter,
|
|||
return -EINVAL;
|
||||
|
||||
switch (action) {
|
||||
case MCHP_TC_SYNAPSE_ACTION_NONE:
|
||||
case COUNTER_SYNAPSE_ACTION_NONE:
|
||||
edge = ATMEL_TC_ETRGEDG_NONE;
|
||||
break;
|
||||
case MCHP_TC_SYNAPSE_ACTION_RISING_EDGE:
|
||||
case COUNTER_SYNAPSE_ACTION_RISING_EDGE:
|
||||
edge = ATMEL_TC_ETRGEDG_RISING;
|
||||
break;
|
||||
case MCHP_TC_SYNAPSE_ACTION_FALLING_EDGE:
|
||||
case COUNTER_SYNAPSE_ACTION_FALLING_EDGE:
|
||||
edge = ATMEL_TC_ETRGEDG_FALLING;
|
||||
break;
|
||||
case MCHP_TC_SYNAPSE_ACTION_BOTH_EDGE:
|
||||
case COUNTER_SYNAPSE_ACTION_BOTH_EDGES:
|
||||
edge = ATMEL_TC_ETRGEDG_BOTH;
|
||||
break;
|
||||
default:
|
||||
|
@ -240,8 +228,7 @@ static int mchp_tc_count_action_set(struct counter_device *counter,
|
|||
}
|
||||
|
||||
static int mchp_tc_count_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
unsigned long *val)
|
||||
struct counter_count *count, u64 *val)
|
||||
{
|
||||
struct mchp_tc_data *const priv = counter->priv;
|
||||
u32 cnt;
|
||||
|
@ -264,12 +251,12 @@ static struct counter_count mchp_tc_counts[] = {
|
|||
};
|
||||
|
||||
static const struct counter_ops mchp_tc_ops = {
|
||||
.signal_read = mchp_tc_count_signal_read,
|
||||
.count_read = mchp_tc_count_read,
|
||||
.function_get = mchp_tc_count_function_get,
|
||||
.function_set = mchp_tc_count_function_set,
|
||||
.action_get = mchp_tc_count_action_get,
|
||||
.action_set = mchp_tc_count_action_set
|
||||
.signal_read = mchp_tc_count_signal_read,
|
||||
.count_read = mchp_tc_count_read,
|
||||
.function_read = mchp_tc_count_function_read,
|
||||
.function_write = mchp_tc_count_function_write,
|
||||
.action_read = mchp_tc_count_action_read,
|
||||
.action_write = mchp_tc_count_action_write
|
||||
};
|
||||
|
||||
static const struct atmel_tcb_config tcb_rm9200_config = {
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
struct stm32_lptim_cnt {
|
||||
struct counter_device counter;
|
||||
|
@ -107,11 +108,7 @@ static int stm32_lptim_setup(struct stm32_lptim_cnt *priv, int enable)
|
|||
return regmap_update_bits(priv->regmap, STM32_LPTIM_CFGR, mask, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* enum stm32_lptim_cnt_function - enumerates LPTimer counter & encoder modes
|
||||
* @STM32_LPTIM_COUNTER_INCREASE: up count on IN1 rising, falling or both edges
|
||||
* @STM32_LPTIM_ENCODER_BOTH_EDGE: count on both edges (IN1 & IN2 quadrature)
|
||||
*
|
||||
/*
|
||||
* In non-quadrature mode, device counts up on active edge.
|
||||
* In quadrature mode, encoder counting scenarios are as follows:
|
||||
* +---------+----------+--------------------+--------------------+
|
||||
|
@ -129,33 +126,20 @@ static int stm32_lptim_setup(struct stm32_lptim_cnt *priv, int enable)
|
|||
* | edges | Low -> | Up | Down | Down | Up |
|
||||
* +---------+----------+----------+---------+----------+---------+
|
||||
*/
|
||||
enum stm32_lptim_cnt_function {
|
||||
STM32_LPTIM_COUNTER_INCREASE,
|
||||
STM32_LPTIM_ENCODER_BOTH_EDGE,
|
||||
};
|
||||
|
||||
static const enum counter_function stm32_lptim_cnt_functions[] = {
|
||||
[STM32_LPTIM_COUNTER_INCREASE] = COUNTER_FUNCTION_INCREASE,
|
||||
[STM32_LPTIM_ENCODER_BOTH_EDGE] = COUNTER_FUNCTION_QUADRATURE_X4,
|
||||
};
|
||||
|
||||
enum stm32_lptim_synapse_action {
|
||||
STM32_LPTIM_SYNAPSE_ACTION_RISING_EDGE,
|
||||
STM32_LPTIM_SYNAPSE_ACTION_FALLING_EDGE,
|
||||
STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES,
|
||||
STM32_LPTIM_SYNAPSE_ACTION_NONE,
|
||||
COUNTER_FUNCTION_INCREASE,
|
||||
COUNTER_FUNCTION_QUADRATURE_X4,
|
||||
};
|
||||
|
||||
static const enum counter_synapse_action stm32_lptim_cnt_synapse_actions[] = {
|
||||
/* Index must match with stm32_lptim_cnt_polarity[] (priv->polarity) */
|
||||
[STM32_LPTIM_SYNAPSE_ACTION_RISING_EDGE] = COUNTER_SYNAPSE_ACTION_RISING_EDGE,
|
||||
[STM32_LPTIM_SYNAPSE_ACTION_FALLING_EDGE] = COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
|
||||
[STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES] = COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
|
||||
[STM32_LPTIM_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE,
|
||||
COUNTER_SYNAPSE_ACTION_RISING_EDGE,
|
||||
COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
|
||||
COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
|
||||
COUNTER_SYNAPSE_ACTION_NONE,
|
||||
};
|
||||
|
||||
static int stm32_lptim_cnt_read(struct counter_device *counter,
|
||||
struct counter_count *count, unsigned long *val)
|
||||
struct counter_count *count, u64 *val)
|
||||
{
|
||||
struct stm32_lptim_cnt *const priv = counter->priv;
|
||||
u32 cnt;
|
||||
|
@ -170,28 +154,28 @@ static int stm32_lptim_cnt_read(struct counter_device *counter,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_lptim_cnt_function_get(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
size_t *function)
|
||||
static int stm32_lptim_cnt_function_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
enum counter_function *function)
|
||||
{
|
||||
struct stm32_lptim_cnt *const priv = counter->priv;
|
||||
|
||||
if (!priv->quadrature_mode) {
|
||||
*function = STM32_LPTIM_COUNTER_INCREASE;
|
||||
*function = COUNTER_FUNCTION_INCREASE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (priv->polarity == STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES) {
|
||||
*function = STM32_LPTIM_ENCODER_BOTH_EDGE;
|
||||
if (priv->polarity == STM32_LPTIM_CKPOL_BOTH_EDGES) {
|
||||
*function = COUNTER_FUNCTION_QUADRATURE_X4;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int stm32_lptim_cnt_function_set(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
size_t function)
|
||||
static int stm32_lptim_cnt_function_write(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
enum counter_function function)
|
||||
{
|
||||
struct stm32_lptim_cnt *const priv = counter->priv;
|
||||
|
||||
|
@ -199,12 +183,12 @@ static int stm32_lptim_cnt_function_set(struct counter_device *counter,
|
|||
return -EBUSY;
|
||||
|
||||
switch (function) {
|
||||
case STM32_LPTIM_COUNTER_INCREASE:
|
||||
case COUNTER_FUNCTION_INCREASE:
|
||||
priv->quadrature_mode = 0;
|
||||
return 0;
|
||||
case STM32_LPTIM_ENCODER_BOTH_EDGE:
|
||||
case COUNTER_FUNCTION_QUADRATURE_X4:
|
||||
priv->quadrature_mode = 1;
|
||||
priv->polarity = STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES;
|
||||
priv->polarity = STM32_LPTIM_CKPOL_BOTH_EDGES;
|
||||
return 0;
|
||||
default:
|
||||
/* should never reach this path */
|
||||
|
@ -212,9 +196,9 @@ static int stm32_lptim_cnt_function_set(struct counter_device *counter,
|
|||
}
|
||||
}
|
||||
|
||||
static ssize_t stm32_lptim_cnt_enable_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
void *private, char *buf)
|
||||
static int stm32_lptim_cnt_enable_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
u8 *enable)
|
||||
{
|
||||
struct stm32_lptim_cnt *const priv = counter->priv;
|
||||
int ret;
|
||||
|
@ -223,22 +207,18 @@ static ssize_t stm32_lptim_cnt_enable_read(struct counter_device *counter,
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%u\n", ret);
|
||||
*enable = ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t stm32_lptim_cnt_enable_write(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
void *private,
|
||||
const char *buf, size_t len)
|
||||
static int stm32_lptim_cnt_enable_write(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
u8 enable)
|
||||
{
|
||||
struct stm32_lptim_cnt *const priv = counter->priv;
|
||||
bool enable;
|
||||
int ret;
|
||||
|
||||
ret = kstrtobool(buf, &enable);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Check nobody uses the timer, or already disabled/enabled */
|
||||
ret = stm32_lptim_is_enabled(priv);
|
||||
if ((ret < 0) || (!ret && !enable))
|
||||
|
@ -254,78 +234,81 @@ static ssize_t stm32_lptim_cnt_enable_write(struct counter_device *counter,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
return len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t stm32_lptim_cnt_ceiling_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
void *private, char *buf)
|
||||
static int stm32_lptim_cnt_ceiling_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
u64 *ceiling)
|
||||
{
|
||||
struct stm32_lptim_cnt *const priv = counter->priv;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", priv->ceiling);
|
||||
*ceiling = priv->ceiling;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t stm32_lptim_cnt_ceiling_write(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
void *private,
|
||||
const char *buf, size_t len)
|
||||
static int stm32_lptim_cnt_ceiling_write(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
u64 ceiling)
|
||||
{
|
||||
struct stm32_lptim_cnt *const priv = counter->priv;
|
||||
unsigned int ceiling;
|
||||
int ret;
|
||||
|
||||
if (stm32_lptim_is_enabled(priv))
|
||||
return -EBUSY;
|
||||
|
||||
ret = kstrtouint(buf, 0, &ceiling);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (ceiling > STM32_LPTIM_MAX_ARR)
|
||||
return -ERANGE;
|
||||
|
||||
priv->ceiling = ceiling;
|
||||
|
||||
return len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct counter_count_ext stm32_lptim_cnt_ext[] = {
|
||||
{
|
||||
.name = "enable",
|
||||
.read = stm32_lptim_cnt_enable_read,
|
||||
.write = stm32_lptim_cnt_enable_write
|
||||
},
|
||||
{
|
||||
.name = "ceiling",
|
||||
.read = stm32_lptim_cnt_ceiling_read,
|
||||
.write = stm32_lptim_cnt_ceiling_write
|
||||
},
|
||||
static struct counter_comp stm32_lptim_cnt_ext[] = {
|
||||
COUNTER_COMP_ENABLE(stm32_lptim_cnt_enable_read,
|
||||
stm32_lptim_cnt_enable_write),
|
||||
COUNTER_COMP_CEILING(stm32_lptim_cnt_ceiling_read,
|
||||
stm32_lptim_cnt_ceiling_write),
|
||||
};
|
||||
|
||||
static int stm32_lptim_cnt_action_get(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
struct counter_synapse *synapse,
|
||||
size_t *action)
|
||||
static int stm32_lptim_cnt_action_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
struct counter_synapse *synapse,
|
||||
enum counter_synapse_action *action)
|
||||
{
|
||||
struct stm32_lptim_cnt *const priv = counter->priv;
|
||||
size_t function;
|
||||
enum counter_function function;
|
||||
int err;
|
||||
|
||||
err = stm32_lptim_cnt_function_get(counter, count, &function);
|
||||
err = stm32_lptim_cnt_function_read(counter, count, &function);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
switch (function) {
|
||||
case STM32_LPTIM_COUNTER_INCREASE:
|
||||
case COUNTER_FUNCTION_INCREASE:
|
||||
/* LP Timer acts as up-counter on input 1 */
|
||||
if (synapse->signal->id == count->synapses[0].signal->id)
|
||||
*action = priv->polarity;
|
||||
else
|
||||
*action = STM32_LPTIM_SYNAPSE_ACTION_NONE;
|
||||
return 0;
|
||||
case STM32_LPTIM_ENCODER_BOTH_EDGE:
|
||||
*action = priv->polarity;
|
||||
if (synapse->signal->id != count->synapses[0].signal->id) {
|
||||
*action = COUNTER_SYNAPSE_ACTION_NONE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (priv->polarity) {
|
||||
case STM32_LPTIM_CKPOL_RISING_EDGE:
|
||||
*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
|
||||
return 0;
|
||||
case STM32_LPTIM_CKPOL_FALLING_EDGE:
|
||||
*action = COUNTER_SYNAPSE_ACTION_FALLING_EDGE;
|
||||
return 0;
|
||||
case STM32_LPTIM_CKPOL_BOTH_EDGES:
|
||||
*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
|
||||
return 0;
|
||||
default:
|
||||
/* should never reach this path */
|
||||
return -EINVAL;
|
||||
}
|
||||
case COUNTER_FUNCTION_QUADRATURE_X4:
|
||||
*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
|
||||
return 0;
|
||||
default:
|
||||
/* should never reach this path */
|
||||
|
@ -333,43 +316,48 @@ static int stm32_lptim_cnt_action_get(struct counter_device *counter,
|
|||
}
|
||||
}
|
||||
|
||||
static int stm32_lptim_cnt_action_set(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
struct counter_synapse *synapse,
|
||||
size_t action)
|
||||
static int stm32_lptim_cnt_action_write(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
struct counter_synapse *synapse,
|
||||
enum counter_synapse_action action)
|
||||
{
|
||||
struct stm32_lptim_cnt *const priv = counter->priv;
|
||||
size_t function;
|
||||
enum counter_function function;
|
||||
int err;
|
||||
|
||||
if (stm32_lptim_is_enabled(priv))
|
||||
return -EBUSY;
|
||||
|
||||
err = stm32_lptim_cnt_function_get(counter, count, &function);
|
||||
err = stm32_lptim_cnt_function_read(counter, count, &function);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* only set polarity when in counter mode (on input 1) */
|
||||
if (function == STM32_LPTIM_COUNTER_INCREASE
|
||||
&& synapse->signal->id == count->synapses[0].signal->id) {
|
||||
switch (action) {
|
||||
case STM32_LPTIM_SYNAPSE_ACTION_RISING_EDGE:
|
||||
case STM32_LPTIM_SYNAPSE_ACTION_FALLING_EDGE:
|
||||
case STM32_LPTIM_SYNAPSE_ACTION_BOTH_EDGES:
|
||||
priv->polarity = action;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (function != COUNTER_FUNCTION_INCREASE
|
||||
|| synapse->signal->id != count->synapses[0].signal->id)
|
||||
return -EINVAL;
|
||||
|
||||
return -EINVAL;
|
||||
switch (action) {
|
||||
case COUNTER_SYNAPSE_ACTION_RISING_EDGE:
|
||||
priv->polarity = STM32_LPTIM_CKPOL_RISING_EDGE;
|
||||
return 0;
|
||||
case COUNTER_SYNAPSE_ACTION_FALLING_EDGE:
|
||||
priv->polarity = STM32_LPTIM_CKPOL_FALLING_EDGE;
|
||||
return 0;
|
||||
case COUNTER_SYNAPSE_ACTION_BOTH_EDGES:
|
||||
priv->polarity = STM32_LPTIM_CKPOL_BOTH_EDGES;
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct counter_ops stm32_lptim_cnt_ops = {
|
||||
.count_read = stm32_lptim_cnt_read,
|
||||
.function_get = stm32_lptim_cnt_function_get,
|
||||
.function_set = stm32_lptim_cnt_function_set,
|
||||
.action_get = stm32_lptim_cnt_action_get,
|
||||
.action_set = stm32_lptim_cnt_action_set,
|
||||
.function_read = stm32_lptim_cnt_function_read,
|
||||
.function_write = stm32_lptim_cnt_function_write,
|
||||
.action_read = stm32_lptim_cnt_action_read,
|
||||
.action_write = stm32_lptim_cnt_action_write,
|
||||
};
|
||||
|
||||
static struct counter_signal stm32_lptim_cnt_signals[] = {
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#define TIM_CCMR_CCXS (BIT(8) | BIT(0))
|
||||
#define TIM_CCMR_MASK (TIM_CCMR_CC1S | TIM_CCMR_CC2S | \
|
||||
|
@ -36,29 +37,15 @@ struct stm32_timer_cnt {
|
|||
struct stm32_timer_regs bak;
|
||||
};
|
||||
|
||||
/**
|
||||
* enum stm32_count_function - enumerates stm32 timer counter encoder modes
|
||||
* @STM32_COUNT_SLAVE_MODE_DISABLED: counts on internal clock when CEN=1
|
||||
* @STM32_COUNT_ENCODER_MODE_1: counts TI1FP1 edges, depending on TI2FP2 level
|
||||
* @STM32_COUNT_ENCODER_MODE_2: counts TI2FP2 edges, depending on TI1FP1 level
|
||||
* @STM32_COUNT_ENCODER_MODE_3: counts on both TI1FP1 and TI2FP2 edges
|
||||
*/
|
||||
enum stm32_count_function {
|
||||
STM32_COUNT_SLAVE_MODE_DISABLED,
|
||||
STM32_COUNT_ENCODER_MODE_1,
|
||||
STM32_COUNT_ENCODER_MODE_2,
|
||||
STM32_COUNT_ENCODER_MODE_3,
|
||||
};
|
||||
|
||||
static const enum counter_function stm32_count_functions[] = {
|
||||
[STM32_COUNT_SLAVE_MODE_DISABLED] = COUNTER_FUNCTION_INCREASE,
|
||||
[STM32_COUNT_ENCODER_MODE_1] = COUNTER_FUNCTION_QUADRATURE_X2_A,
|
||||
[STM32_COUNT_ENCODER_MODE_2] = COUNTER_FUNCTION_QUADRATURE_X2_B,
|
||||
[STM32_COUNT_ENCODER_MODE_3] = COUNTER_FUNCTION_QUADRATURE_X4,
|
||||
COUNTER_FUNCTION_INCREASE,
|
||||
COUNTER_FUNCTION_QUADRATURE_X2_A,
|
||||
COUNTER_FUNCTION_QUADRATURE_X2_B,
|
||||
COUNTER_FUNCTION_QUADRATURE_X4,
|
||||
};
|
||||
|
||||
static int stm32_count_read(struct counter_device *counter,
|
||||
struct counter_count *count, unsigned long *val)
|
||||
struct counter_count *count, u64 *val)
|
||||
{
|
||||
struct stm32_timer_cnt *const priv = counter->priv;
|
||||
u32 cnt;
|
||||
|
@ -70,8 +57,7 @@ static int stm32_count_read(struct counter_device *counter,
|
|||
}
|
||||
|
||||
static int stm32_count_write(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
const unsigned long val)
|
||||
struct counter_count *count, const u64 val)
|
||||
{
|
||||
struct stm32_timer_cnt *const priv = counter->priv;
|
||||
u32 ceiling;
|
||||
|
@ -83,9 +69,9 @@ static int stm32_count_write(struct counter_device *counter,
|
|||
return regmap_write(priv->regmap, TIM_CNT, val);
|
||||
}
|
||||
|
||||
static int stm32_count_function_get(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
size_t *function)
|
||||
static int stm32_count_function_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
enum counter_function *function)
|
||||
{
|
||||
struct stm32_timer_cnt *const priv = counter->priv;
|
||||
u32 smcr;
|
||||
|
@ -93,42 +79,42 @@ static int stm32_count_function_get(struct counter_device *counter,
|
|||
regmap_read(priv->regmap, TIM_SMCR, &smcr);
|
||||
|
||||
switch (smcr & TIM_SMCR_SMS) {
|
||||
case 0:
|
||||
*function = STM32_COUNT_SLAVE_MODE_DISABLED;
|
||||
case TIM_SMCR_SMS_SLAVE_MODE_DISABLED:
|
||||
*function = COUNTER_FUNCTION_INCREASE;
|
||||
return 0;
|
||||
case 1:
|
||||
*function = STM32_COUNT_ENCODER_MODE_1;
|
||||
case TIM_SMCR_SMS_ENCODER_MODE_1:
|
||||
*function = COUNTER_FUNCTION_QUADRATURE_X2_A;
|
||||
return 0;
|
||||
case 2:
|
||||
*function = STM32_COUNT_ENCODER_MODE_2;
|
||||
case TIM_SMCR_SMS_ENCODER_MODE_2:
|
||||
*function = COUNTER_FUNCTION_QUADRATURE_X2_B;
|
||||
return 0;
|
||||
case 3:
|
||||
*function = STM32_COUNT_ENCODER_MODE_3;
|
||||
case TIM_SMCR_SMS_ENCODER_MODE_3:
|
||||
*function = COUNTER_FUNCTION_QUADRATURE_X4;
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int stm32_count_function_set(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
size_t function)
|
||||
static int stm32_count_function_write(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
enum counter_function function)
|
||||
{
|
||||
struct stm32_timer_cnt *const priv = counter->priv;
|
||||
u32 cr1, sms;
|
||||
|
||||
switch (function) {
|
||||
case STM32_COUNT_SLAVE_MODE_DISABLED:
|
||||
sms = 0;
|
||||
case COUNTER_FUNCTION_INCREASE:
|
||||
sms = TIM_SMCR_SMS_SLAVE_MODE_DISABLED;
|
||||
break;
|
||||
case STM32_COUNT_ENCODER_MODE_1:
|
||||
sms = 1;
|
||||
case COUNTER_FUNCTION_QUADRATURE_X2_A:
|
||||
sms = TIM_SMCR_SMS_ENCODER_MODE_1;
|
||||
break;
|
||||
case STM32_COUNT_ENCODER_MODE_2:
|
||||
sms = 2;
|
||||
case COUNTER_FUNCTION_QUADRATURE_X2_B:
|
||||
sms = TIM_SMCR_SMS_ENCODER_MODE_2;
|
||||
break;
|
||||
case STM32_COUNT_ENCODER_MODE_3:
|
||||
sms = 3;
|
||||
case COUNTER_FUNCTION_QUADRATURE_X4:
|
||||
sms = TIM_SMCR_SMS_ENCODER_MODE_3;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
|
@ -150,44 +136,37 @@ static int stm32_count_function_set(struct counter_device *counter,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t stm32_count_direction_read(struct counter_device *counter,
|
||||
static int stm32_count_direction_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
void *private, char *buf)
|
||||
enum counter_count_direction *direction)
|
||||
{
|
||||
struct stm32_timer_cnt *const priv = counter->priv;
|
||||
const char *direction;
|
||||
u32 cr1;
|
||||
|
||||
regmap_read(priv->regmap, TIM_CR1, &cr1);
|
||||
direction = (cr1 & TIM_CR1_DIR) ? "backward" : "forward";
|
||||
*direction = (cr1 & TIM_CR1_DIR) ? COUNTER_COUNT_DIRECTION_BACKWARD :
|
||||
COUNTER_COUNT_DIRECTION_FORWARD;
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%s\n", direction);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t stm32_count_ceiling_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
void *private, char *buf)
|
||||
static int stm32_count_ceiling_read(struct counter_device *counter,
|
||||
struct counter_count *count, u64 *ceiling)
|
||||
{
|
||||
struct stm32_timer_cnt *const priv = counter->priv;
|
||||
u32 arr;
|
||||
|
||||
regmap_read(priv->regmap, TIM_ARR, &arr);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", arr);
|
||||
*ceiling = arr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t stm32_count_ceiling_write(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
void *private,
|
||||
const char *buf, size_t len)
|
||||
static int stm32_count_ceiling_write(struct counter_device *counter,
|
||||
struct counter_count *count, u64 ceiling)
|
||||
{
|
||||
struct stm32_timer_cnt *const priv = counter->priv;
|
||||
unsigned int ceiling;
|
||||
int ret;
|
||||
|
||||
ret = kstrtouint(buf, 0, &ceiling);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (ceiling > priv->max_arr)
|
||||
return -ERANGE;
|
||||
|
@ -196,34 +175,27 @@ static ssize_t stm32_count_ceiling_write(struct counter_device *counter,
|
|||
regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_ARPE, 0);
|
||||
regmap_write(priv->regmap, TIM_ARR, ceiling);
|
||||
|
||||
return len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t stm32_count_enable_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
void *private, char *buf)
|
||||
static int stm32_count_enable_read(struct counter_device *counter,
|
||||
struct counter_count *count, u8 *enable)
|
||||
{
|
||||
struct stm32_timer_cnt *const priv = counter->priv;
|
||||
u32 cr1;
|
||||
|
||||
regmap_read(priv->regmap, TIM_CR1, &cr1);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", (bool)(cr1 & TIM_CR1_CEN));
|
||||
*enable = cr1 & TIM_CR1_CEN;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t stm32_count_enable_write(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
void *private,
|
||||
const char *buf, size_t len)
|
||||
static int stm32_count_enable_write(struct counter_device *counter,
|
||||
struct counter_count *count, u8 enable)
|
||||
{
|
||||
struct stm32_timer_cnt *const priv = counter->priv;
|
||||
int err;
|
||||
u32 cr1;
|
||||
bool enable;
|
||||
|
||||
err = kstrtobool(buf, &enable);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (enable) {
|
||||
regmap_read(priv->regmap, TIM_CR1, &cr1);
|
||||
|
@ -242,70 +214,55 @@ static ssize_t stm32_count_enable_write(struct counter_device *counter,
|
|||
/* Keep enabled state to properly handle low power states */
|
||||
priv->enabled = enable;
|
||||
|
||||
return len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct counter_count_ext stm32_count_ext[] = {
|
||||
{
|
||||
.name = "direction",
|
||||
.read = stm32_count_direction_read,
|
||||
},
|
||||
{
|
||||
.name = "enable",
|
||||
.read = stm32_count_enable_read,
|
||||
.write = stm32_count_enable_write
|
||||
},
|
||||
{
|
||||
.name = "ceiling",
|
||||
.read = stm32_count_ceiling_read,
|
||||
.write = stm32_count_ceiling_write
|
||||
},
|
||||
};
|
||||
|
||||
enum stm32_synapse_action {
|
||||
STM32_SYNAPSE_ACTION_NONE,
|
||||
STM32_SYNAPSE_ACTION_BOTH_EDGES
|
||||
static struct counter_comp stm32_count_ext[] = {
|
||||
COUNTER_COMP_DIRECTION(stm32_count_direction_read),
|
||||
COUNTER_COMP_ENABLE(stm32_count_enable_read, stm32_count_enable_write),
|
||||
COUNTER_COMP_CEILING(stm32_count_ceiling_read,
|
||||
stm32_count_ceiling_write),
|
||||
};
|
||||
|
||||
static const enum counter_synapse_action stm32_synapse_actions[] = {
|
||||
[STM32_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE,
|
||||
[STM32_SYNAPSE_ACTION_BOTH_EDGES] = COUNTER_SYNAPSE_ACTION_BOTH_EDGES
|
||||
COUNTER_SYNAPSE_ACTION_NONE,
|
||||
COUNTER_SYNAPSE_ACTION_BOTH_EDGES
|
||||
};
|
||||
|
||||
static int stm32_action_get(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
struct counter_synapse *synapse,
|
||||
size_t *action)
|
||||
static int stm32_action_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
struct counter_synapse *synapse,
|
||||
enum counter_synapse_action *action)
|
||||
{
|
||||
size_t function;
|
||||
enum counter_function function;
|
||||
int err;
|
||||
|
||||
err = stm32_count_function_get(counter, count, &function);
|
||||
err = stm32_count_function_read(counter, count, &function);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
switch (function) {
|
||||
case STM32_COUNT_SLAVE_MODE_DISABLED:
|
||||
case COUNTER_FUNCTION_INCREASE:
|
||||
/* counts on internal clock when CEN=1 */
|
||||
*action = STM32_SYNAPSE_ACTION_NONE;
|
||||
*action = COUNTER_SYNAPSE_ACTION_NONE;
|
||||
return 0;
|
||||
case STM32_COUNT_ENCODER_MODE_1:
|
||||
case COUNTER_FUNCTION_QUADRATURE_X2_A:
|
||||
/* counts up/down on TI1FP1 edge depending on TI2FP2 level */
|
||||
if (synapse->signal->id == count->synapses[0].signal->id)
|
||||
*action = STM32_SYNAPSE_ACTION_BOTH_EDGES;
|
||||
*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
|
||||
else
|
||||
*action = STM32_SYNAPSE_ACTION_NONE;
|
||||
*action = COUNTER_SYNAPSE_ACTION_NONE;
|
||||
return 0;
|
||||
case STM32_COUNT_ENCODER_MODE_2:
|
||||
case COUNTER_FUNCTION_QUADRATURE_X2_B:
|
||||
/* counts up/down on TI2FP2 edge depending on TI1FP1 level */
|
||||
if (synapse->signal->id == count->synapses[1].signal->id)
|
||||
*action = STM32_SYNAPSE_ACTION_BOTH_EDGES;
|
||||
*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
|
||||
else
|
||||
*action = STM32_SYNAPSE_ACTION_NONE;
|
||||
*action = COUNTER_SYNAPSE_ACTION_NONE;
|
||||
return 0;
|
||||
case STM32_COUNT_ENCODER_MODE_3:
|
||||
case COUNTER_FUNCTION_QUADRATURE_X4:
|
||||
/* counts up/down on both TI1FP1 and TI2FP2 edges */
|
||||
*action = STM32_SYNAPSE_ACTION_BOTH_EDGES;
|
||||
*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
|
@ -315,9 +272,9 @@ static int stm32_action_get(struct counter_device *counter,
|
|||
static const struct counter_ops stm32_timer_cnt_ops = {
|
||||
.count_read = stm32_count_read,
|
||||
.count_write = stm32_count_write,
|
||||
.function_get = stm32_count_function_get,
|
||||
.function_set = stm32_count_function_set,
|
||||
.action_get = stm32_action_get,
|
||||
.function_read = stm32_count_function_read,
|
||||
.function_write = stm32_count_function_write,
|
||||
.action_read = stm32_action_read,
|
||||
};
|
||||
|
||||
static struct counter_signal stm32_signals[] = {
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/* 32-bit registers */
|
||||
#define QPOSCNT 0x0
|
||||
|
@ -73,19 +74,13 @@ enum {
|
|||
};
|
||||
|
||||
/* Position Counter Input Modes */
|
||||
enum {
|
||||
enum ti_eqep_count_func {
|
||||
TI_EQEP_COUNT_FUNC_QUAD_COUNT,
|
||||
TI_EQEP_COUNT_FUNC_DIR_COUNT,
|
||||
TI_EQEP_COUNT_FUNC_UP_COUNT,
|
||||
TI_EQEP_COUNT_FUNC_DOWN_COUNT,
|
||||
};
|
||||
|
||||
enum {
|
||||
TI_EQEP_SYNAPSE_ACTION_BOTH_EDGES,
|
||||
TI_EQEP_SYNAPSE_ACTION_RISING_EDGE,
|
||||
TI_EQEP_SYNAPSE_ACTION_NONE,
|
||||
};
|
||||
|
||||
struct ti_eqep_cnt {
|
||||
struct counter_device counter;
|
||||
struct regmap *regmap32;
|
||||
|
@ -93,7 +88,7 @@ struct ti_eqep_cnt {
|
|||
};
|
||||
|
||||
static int ti_eqep_count_read(struct counter_device *counter,
|
||||
struct counter_count *count, unsigned long *val)
|
||||
struct counter_count *count, u64 *val)
|
||||
{
|
||||
struct ti_eqep_cnt *priv = counter->priv;
|
||||
u32 cnt;
|
||||
|
@ -105,7 +100,7 @@ static int ti_eqep_count_read(struct counter_device *counter,
|
|||
}
|
||||
|
||||
static int ti_eqep_count_write(struct counter_device *counter,
|
||||
struct counter_count *count, unsigned long val)
|
||||
struct counter_count *count, u64 val)
|
||||
{
|
||||
struct ti_eqep_cnt *priv = counter->priv;
|
||||
u32 max;
|
||||
|
@ -117,64 +112,100 @@ static int ti_eqep_count_write(struct counter_device *counter,
|
|||
return regmap_write(priv->regmap32, QPOSCNT, val);
|
||||
}
|
||||
|
||||
static int ti_eqep_function_get(struct counter_device *counter,
|
||||
struct counter_count *count, size_t *function)
|
||||
static int ti_eqep_function_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
enum counter_function *function)
|
||||
{
|
||||
struct ti_eqep_cnt *priv = counter->priv;
|
||||
u32 qdecctl;
|
||||
|
||||
regmap_read(priv->regmap16, QDECCTL, &qdecctl);
|
||||
*function = (qdecctl & QDECCTL_QSRC) >> QDECCTL_QSRC_SHIFT;
|
||||
|
||||
switch ((qdecctl & QDECCTL_QSRC) >> QDECCTL_QSRC_SHIFT) {
|
||||
case TI_EQEP_COUNT_FUNC_QUAD_COUNT:
|
||||
*function = COUNTER_FUNCTION_QUADRATURE_X4;
|
||||
break;
|
||||
case TI_EQEP_COUNT_FUNC_DIR_COUNT:
|
||||
*function = COUNTER_FUNCTION_PULSE_DIRECTION;
|
||||
break;
|
||||
case TI_EQEP_COUNT_FUNC_UP_COUNT:
|
||||
*function = COUNTER_FUNCTION_INCREASE;
|
||||
break;
|
||||
case TI_EQEP_COUNT_FUNC_DOWN_COUNT:
|
||||
*function = COUNTER_FUNCTION_DECREASE;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ti_eqep_function_set(struct counter_device *counter,
|
||||
struct counter_count *count, size_t function)
|
||||
static int ti_eqep_function_write(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
enum counter_function function)
|
||||
{
|
||||
struct ti_eqep_cnt *priv = counter->priv;
|
||||
enum ti_eqep_count_func qsrc;
|
||||
|
||||
switch (function) {
|
||||
case COUNTER_FUNCTION_QUADRATURE_X4:
|
||||
qsrc = TI_EQEP_COUNT_FUNC_QUAD_COUNT;
|
||||
break;
|
||||
case COUNTER_FUNCTION_PULSE_DIRECTION:
|
||||
qsrc = TI_EQEP_COUNT_FUNC_DIR_COUNT;
|
||||
break;
|
||||
case COUNTER_FUNCTION_INCREASE:
|
||||
qsrc = TI_EQEP_COUNT_FUNC_UP_COUNT;
|
||||
break;
|
||||
case COUNTER_FUNCTION_DECREASE:
|
||||
qsrc = TI_EQEP_COUNT_FUNC_DOWN_COUNT;
|
||||
break;
|
||||
default:
|
||||
/* should never reach this path */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return regmap_write_bits(priv->regmap16, QDECCTL, QDECCTL_QSRC,
|
||||
function << QDECCTL_QSRC_SHIFT);
|
||||
qsrc << QDECCTL_QSRC_SHIFT);
|
||||
}
|
||||
|
||||
static int ti_eqep_action_get(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
struct counter_synapse *synapse, size_t *action)
|
||||
static int ti_eqep_action_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
struct counter_synapse *synapse,
|
||||
enum counter_synapse_action *action)
|
||||
{
|
||||
struct ti_eqep_cnt *priv = counter->priv;
|
||||
size_t function;
|
||||
enum counter_function function;
|
||||
u32 qdecctl;
|
||||
int err;
|
||||
|
||||
err = ti_eqep_function_get(counter, count, &function);
|
||||
err = ti_eqep_function_read(counter, count, &function);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
switch (function) {
|
||||
case TI_EQEP_COUNT_FUNC_QUAD_COUNT:
|
||||
case COUNTER_FUNCTION_QUADRATURE_X4:
|
||||
/* In quadrature mode, the rising and falling edge of both
|
||||
* QEPA and QEPB trigger QCLK.
|
||||
*/
|
||||
*action = TI_EQEP_SYNAPSE_ACTION_BOTH_EDGES;
|
||||
*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
|
||||
return 0;
|
||||
case TI_EQEP_COUNT_FUNC_DIR_COUNT:
|
||||
case COUNTER_FUNCTION_PULSE_DIRECTION:
|
||||
/* In direction-count mode only rising edge of QEPA is counted
|
||||
* and QEPB gives direction.
|
||||
*/
|
||||
switch (synapse->signal->id) {
|
||||
case TI_EQEP_SIGNAL_QEPA:
|
||||
*action = TI_EQEP_SYNAPSE_ACTION_RISING_EDGE;
|
||||
*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
|
||||
return 0;
|
||||
case TI_EQEP_SIGNAL_QEPB:
|
||||
*action = TI_EQEP_SYNAPSE_ACTION_NONE;
|
||||
*action = COUNTER_SYNAPSE_ACTION_NONE;
|
||||
return 0;
|
||||
default:
|
||||
/* should never reach this path */
|
||||
return -EINVAL;
|
||||
}
|
||||
case TI_EQEP_COUNT_FUNC_UP_COUNT:
|
||||
case TI_EQEP_COUNT_FUNC_DOWN_COUNT:
|
||||
case COUNTER_FUNCTION_INCREASE:
|
||||
case COUNTER_FUNCTION_DECREASE:
|
||||
/* In up/down-count modes only QEPA is counted and QEPB is not
|
||||
* used.
|
||||
*/
|
||||
|
@ -185,12 +216,12 @@ static int ti_eqep_action_get(struct counter_device *counter,
|
|||
return err;
|
||||
|
||||
if (qdecctl & QDECCTL_XCR)
|
||||
*action = TI_EQEP_SYNAPSE_ACTION_BOTH_EDGES;
|
||||
*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
|
||||
else
|
||||
*action = TI_EQEP_SYNAPSE_ACTION_RISING_EDGE;
|
||||
*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
|
||||
return 0;
|
||||
case TI_EQEP_SIGNAL_QEPB:
|
||||
*action = TI_EQEP_SYNAPSE_ACTION_NONE;
|
||||
*action = COUNTER_SYNAPSE_ACTION_NONE;
|
||||
return 0;
|
||||
default:
|
||||
/* should never reach this path */
|
||||
|
@ -205,82 +236,67 @@ static int ti_eqep_action_get(struct counter_device *counter,
|
|||
static const struct counter_ops ti_eqep_counter_ops = {
|
||||
.count_read = ti_eqep_count_read,
|
||||
.count_write = ti_eqep_count_write,
|
||||
.function_get = ti_eqep_function_get,
|
||||
.function_set = ti_eqep_function_set,
|
||||
.action_get = ti_eqep_action_get,
|
||||
.function_read = ti_eqep_function_read,
|
||||
.function_write = ti_eqep_function_write,
|
||||
.action_read = ti_eqep_action_read,
|
||||
};
|
||||
|
||||
static ssize_t ti_eqep_position_ceiling_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
void *ext_priv, char *buf)
|
||||
static int ti_eqep_position_ceiling_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
u64 *ceiling)
|
||||
{
|
||||
struct ti_eqep_cnt *priv = counter->priv;
|
||||
u32 qposmax;
|
||||
|
||||
regmap_read(priv->regmap32, QPOSMAX, &qposmax);
|
||||
|
||||
return sprintf(buf, "%u\n", qposmax);
|
||||
*ceiling = qposmax;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t ti_eqep_position_ceiling_write(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
void *ext_priv, const char *buf,
|
||||
size_t len)
|
||||
static int ti_eqep_position_ceiling_write(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
u64 ceiling)
|
||||
{
|
||||
struct ti_eqep_cnt *priv = counter->priv;
|
||||
int err;
|
||||
u32 res;
|
||||
|
||||
err = kstrtouint(buf, 0, &res);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (ceiling != (u32)ceiling)
|
||||
return -ERANGE;
|
||||
|
||||
regmap_write(priv->regmap32, QPOSMAX, res);
|
||||
regmap_write(priv->regmap32, QPOSMAX, ceiling);
|
||||
|
||||
return len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t ti_eqep_position_enable_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
void *ext_priv, char *buf)
|
||||
static int ti_eqep_position_enable_read(struct counter_device *counter,
|
||||
struct counter_count *count, u8 *enable)
|
||||
{
|
||||
struct ti_eqep_cnt *priv = counter->priv;
|
||||
u32 qepctl;
|
||||
|
||||
regmap_read(priv->regmap16, QEPCTL, &qepctl);
|
||||
|
||||
return sprintf(buf, "%u\n", !!(qepctl & QEPCTL_PHEN));
|
||||
*enable = !!(qepctl & QEPCTL_PHEN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t ti_eqep_position_enable_write(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
void *ext_priv, const char *buf,
|
||||
size_t len)
|
||||
static int ti_eqep_position_enable_write(struct counter_device *counter,
|
||||
struct counter_count *count, u8 enable)
|
||||
{
|
||||
struct ti_eqep_cnt *priv = counter->priv;
|
||||
int err;
|
||||
bool res;
|
||||
|
||||
err = kstrtobool(buf, &res);
|
||||
if (err < 0)
|
||||
return err;
|
||||
regmap_write_bits(priv->regmap16, QEPCTL, QEPCTL_PHEN, enable ? -1 : 0);
|
||||
|
||||
regmap_write_bits(priv->regmap16, QEPCTL, QEPCTL_PHEN, res ? -1 : 0);
|
||||
|
||||
return len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct counter_count_ext ti_eqep_position_ext[] = {
|
||||
{
|
||||
.name = "ceiling",
|
||||
.read = ti_eqep_position_ceiling_read,
|
||||
.write = ti_eqep_position_ceiling_write,
|
||||
},
|
||||
{
|
||||
.name = "enable",
|
||||
.read = ti_eqep_position_enable_read,
|
||||
.write = ti_eqep_position_enable_write,
|
||||
},
|
||||
static struct counter_comp ti_eqep_position_ext[] = {
|
||||
COUNTER_COMP_CEILING(ti_eqep_position_ceiling_read,
|
||||
ti_eqep_position_ceiling_write),
|
||||
COUNTER_COMP_ENABLE(ti_eqep_position_enable_read,
|
||||
ti_eqep_position_enable_write),
|
||||
};
|
||||
|
||||
static struct counter_signal ti_eqep_signals[] = {
|
||||
|
@ -295,16 +311,16 @@ static struct counter_signal ti_eqep_signals[] = {
|
|||
};
|
||||
|
||||
static const enum counter_function ti_eqep_position_functions[] = {
|
||||
[TI_EQEP_COUNT_FUNC_QUAD_COUNT] = COUNTER_FUNCTION_QUADRATURE_X4,
|
||||
[TI_EQEP_COUNT_FUNC_DIR_COUNT] = COUNTER_FUNCTION_PULSE_DIRECTION,
|
||||
[TI_EQEP_COUNT_FUNC_UP_COUNT] = COUNTER_FUNCTION_INCREASE,
|
||||
[TI_EQEP_COUNT_FUNC_DOWN_COUNT] = COUNTER_FUNCTION_DECREASE,
|
||||
COUNTER_FUNCTION_QUADRATURE_X4,
|
||||
COUNTER_FUNCTION_PULSE_DIRECTION,
|
||||
COUNTER_FUNCTION_INCREASE,
|
||||
COUNTER_FUNCTION_DECREASE,
|
||||
};
|
||||
|
||||
static const enum counter_synapse_action ti_eqep_position_synapse_actions[] = {
|
||||
[TI_EQEP_SYNAPSE_ACTION_BOTH_EDGES] = COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
|
||||
[TI_EQEP_SYNAPSE_ACTION_RISING_EDGE] = COUNTER_SYNAPSE_ACTION_RISING_EDGE,
|
||||
[TI_EQEP_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE,
|
||||
COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
|
||||
COUNTER_SYNAPSE_ACTION_RISING_EDGE,
|
||||
COUNTER_SYNAPSE_ACTION_NONE,
|
||||
};
|
||||
|
||||
static struct counter_synapse ti_eqep_position_synapses[] = {
|
||||
|
|
|
@ -599,7 +599,7 @@ err_module:
|
|||
module_put(exp_info->owner);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dma_buf_export);
|
||||
EXPORT_SYMBOL_NS_GPL(dma_buf_export, DMA_BUF);
|
||||
|
||||
/**
|
||||
* dma_buf_fd - returns a file descriptor for the given struct dma_buf
|
||||
|
@ -623,7 +623,7 @@ int dma_buf_fd(struct dma_buf *dmabuf, int flags)
|
|||
|
||||
return fd;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dma_buf_fd);
|
||||
EXPORT_SYMBOL_NS_GPL(dma_buf_fd, DMA_BUF);
|
||||
|
||||
/**
|
||||
* dma_buf_get - returns the struct dma_buf related to an fd
|
||||
|
@ -649,7 +649,7 @@ struct dma_buf *dma_buf_get(int fd)
|
|||
|
||||
return file->private_data;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dma_buf_get);
|
||||
EXPORT_SYMBOL_NS_GPL(dma_buf_get, DMA_BUF);
|
||||
|
||||
/**
|
||||
* dma_buf_put - decreases refcount of the buffer
|
||||
|
@ -668,7 +668,7 @@ void dma_buf_put(struct dma_buf *dmabuf)
|
|||
|
||||
fput(dmabuf->file);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dma_buf_put);
|
||||
EXPORT_SYMBOL_NS_GPL(dma_buf_put, DMA_BUF);
|
||||
|
||||
static void mangle_sg_table(struct sg_table *sg_table)
|
||||
{
|
||||
|
@ -799,7 +799,7 @@ err_unlock:
|
|||
dma_buf_detach(dmabuf, attach);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dma_buf_dynamic_attach);
|
||||
EXPORT_SYMBOL_NS_GPL(dma_buf_dynamic_attach, DMA_BUF);
|
||||
|
||||
/**
|
||||
* dma_buf_attach - Wrapper for dma_buf_dynamic_attach
|
||||
|
@ -814,7 +814,7 @@ struct dma_buf_attachment *dma_buf_attach(struct dma_buf *dmabuf,
|
|||
{
|
||||
return dma_buf_dynamic_attach(dmabuf, dev, NULL, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dma_buf_attach);
|
||||
EXPORT_SYMBOL_NS_GPL(dma_buf_attach, DMA_BUF);
|
||||
|
||||
static void __unmap_dma_buf(struct dma_buf_attachment *attach,
|
||||
struct sg_table *sg_table,
|
||||
|
@ -860,7 +860,7 @@ void dma_buf_detach(struct dma_buf *dmabuf, struct dma_buf_attachment *attach)
|
|||
|
||||
kfree(attach);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dma_buf_detach);
|
||||
EXPORT_SYMBOL_NS_GPL(dma_buf_detach, DMA_BUF);
|
||||
|
||||
/**
|
||||
* dma_buf_pin - Lock down the DMA-buf
|
||||
|
@ -890,7 +890,7 @@ int dma_buf_pin(struct dma_buf_attachment *attach)
|
|||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dma_buf_pin);
|
||||
EXPORT_SYMBOL_NS_GPL(dma_buf_pin, DMA_BUF);
|
||||
|
||||
/**
|
||||
* dma_buf_unpin - Unpin a DMA-buf
|
||||
|
@ -911,7 +911,7 @@ void dma_buf_unpin(struct dma_buf_attachment *attach)
|
|||
if (dmabuf->ops->unpin)
|
||||
dmabuf->ops->unpin(attach);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dma_buf_unpin);
|
||||
EXPORT_SYMBOL_NS_GPL(dma_buf_unpin, DMA_BUF);
|
||||
|
||||
/**
|
||||
* dma_buf_map_attachment - Returns the scatterlist table of the attachment;
|
||||
|
@ -1001,7 +1001,7 @@ struct sg_table *dma_buf_map_attachment(struct dma_buf_attachment *attach,
|
|||
#endif /* CONFIG_DMA_API_DEBUG */
|
||||
return sg_table;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dma_buf_map_attachment);
|
||||
EXPORT_SYMBOL_NS_GPL(dma_buf_map_attachment, DMA_BUF);
|
||||
|
||||
/**
|
||||
* dma_buf_unmap_attachment - unmaps and decreases usecount of the buffer;might
|
||||
|
@ -1037,7 +1037,7 @@ void dma_buf_unmap_attachment(struct dma_buf_attachment *attach,
|
|||
!IS_ENABLED(CONFIG_DMABUF_MOVE_NOTIFY))
|
||||
dma_buf_unpin(attach);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dma_buf_unmap_attachment);
|
||||
EXPORT_SYMBOL_NS_GPL(dma_buf_unmap_attachment, DMA_BUF);
|
||||
|
||||
/**
|
||||
* dma_buf_move_notify - notify attachments that DMA-buf is moving
|
||||
|
@ -1057,7 +1057,7 @@ void dma_buf_move_notify(struct dma_buf *dmabuf)
|
|||
if (attach->importer_ops)
|
||||
attach->importer_ops->move_notify(attach);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dma_buf_move_notify);
|
||||
EXPORT_SYMBOL_NS_GPL(dma_buf_move_notify, DMA_BUF);
|
||||
|
||||
/**
|
||||
* DOC: cpu access
|
||||
|
@ -1201,7 +1201,7 @@ int dma_buf_begin_cpu_access(struct dma_buf *dmabuf,
|
|||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dma_buf_begin_cpu_access);
|
||||
EXPORT_SYMBOL_NS_GPL(dma_buf_begin_cpu_access, DMA_BUF);
|
||||
|
||||
/**
|
||||
* dma_buf_end_cpu_access - Must be called after accessing a dma_buf from the
|
||||
|
@ -1229,7 +1229,7 @@ int dma_buf_end_cpu_access(struct dma_buf *dmabuf,
|
|||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dma_buf_end_cpu_access);
|
||||
EXPORT_SYMBOL_NS_GPL(dma_buf_end_cpu_access, DMA_BUF);
|
||||
|
||||
|
||||
/**
|
||||
|
@ -1271,7 +1271,7 @@ int dma_buf_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma,
|
|||
|
||||
return dmabuf->ops->mmap(dmabuf, vma);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dma_buf_mmap);
|
||||
EXPORT_SYMBOL_NS_GPL(dma_buf_mmap, DMA_BUF);
|
||||
|
||||
/**
|
||||
* dma_buf_vmap - Create virtual mapping for the buffer object into kernel
|
||||
|
@ -1325,7 +1325,7 @@ out_unlock:
|
|||
mutex_unlock(&dmabuf->lock);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dma_buf_vmap);
|
||||
EXPORT_SYMBOL_NS_GPL(dma_buf_vmap, DMA_BUF);
|
||||
|
||||
/**
|
||||
* dma_buf_vunmap - Unmap a vmap obtained by dma_buf_vmap.
|
||||
|
@ -1349,7 +1349,7 @@ void dma_buf_vunmap(struct dma_buf *dmabuf, struct dma_buf_map *map)
|
|||
}
|
||||
mutex_unlock(&dmabuf->lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dma_buf_vunmap);
|
||||
EXPORT_SYMBOL_NS_GPL(dma_buf_vunmap, DMA_BUF);
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
static int dma_buf_debug_show(struct seq_file *s, void *unused)
|
||||
|
|
|
@ -23,7 +23,7 @@ config EXTCON_ADC_JACK
|
|||
|
||||
config EXTCON_AXP288
|
||||
tristate "X-Power AXP288 EXTCON support"
|
||||
depends on MFD_AXP20X && USB_SUPPORT && X86 && ACPI
|
||||
depends on MFD_AXP20X && USB_SUPPORT && X86 && ACPI && IOSF_MBI
|
||||
select USB_ROLE_SWITCH
|
||||
help
|
||||
Say Y here to enable support for USB peripheral detection
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
#include <asm/cpu_device_id.h>
|
||||
#include <asm/intel-family.h>
|
||||
#include <asm/iosf_mbi.h>
|
||||
|
||||
/* Power source status register */
|
||||
#define PS_STAT_VBUS_TRIGGER BIT(0)
|
||||
|
@ -215,6 +216,10 @@ static int axp288_handle_chrg_det_event(struct axp288_extcon_info *info)
|
|||
unsigned int cable = info->previous_cable;
|
||||
bool vbus_attach = false;
|
||||
|
||||
ret = iosf_mbi_block_punit_i2c_access();
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
vbus_attach = axp288_get_vbus_attach(info);
|
||||
if (!vbus_attach)
|
||||
goto no_vbus;
|
||||
|
@ -253,6 +258,8 @@ static int axp288_handle_chrg_det_event(struct axp288_extcon_info *info)
|
|||
}
|
||||
|
||||
no_vbus:
|
||||
iosf_mbi_unblock_punit_i2c_access();
|
||||
|
||||
extcon_set_state_sync(info->edev, info->previous_cable, false);
|
||||
if (info->previous_cable == EXTCON_CHG_USB_SDP)
|
||||
extcon_set_state_sync(info->edev, EXTCON_USB, false);
|
||||
|
@ -275,6 +282,8 @@ no_vbus:
|
|||
return 0;
|
||||
|
||||
dev_det_ret:
|
||||
iosf_mbi_unblock_punit_i2c_access();
|
||||
|
||||
if (ret < 0)
|
||||
dev_err(info->dev, "failed to detect BC Mod\n");
|
||||
|
||||
|
@ -305,13 +314,23 @@ static irqreturn_t axp288_extcon_isr(int irq, void *data)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void axp288_extcon_enable(struct axp288_extcon_info *info)
|
||||
static int axp288_extcon_enable(struct axp288_extcon_info *info)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = iosf_mbi_block_punit_i2c_access();
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
regmap_update_bits(info->regmap, AXP288_BC_GLOBAL_REG,
|
||||
BC_GLOBAL_RUN, 0);
|
||||
/* Enable the charger detection logic */
|
||||
regmap_update_bits(info->regmap, AXP288_BC_GLOBAL_REG,
|
||||
BC_GLOBAL_RUN, BC_GLOBAL_RUN);
|
||||
|
||||
iosf_mbi_unblock_punit_i2c_access();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void axp288_put_role_sw(void *data)
|
||||
|
@ -384,10 +403,16 @@ static int axp288_extcon_probe(struct platform_device *pdev)
|
|||
}
|
||||
}
|
||||
|
||||
ret = iosf_mbi_block_punit_i2c_access();
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
info->vbus_attach = axp288_get_vbus_attach(info);
|
||||
|
||||
axp288_extcon_log_rsi(info);
|
||||
|
||||
iosf_mbi_unblock_punit_i2c_access();
|
||||
|
||||
/* Initialize extcon device */
|
||||
info->edev = devm_extcon_dev_allocate(&pdev->dev,
|
||||
axp288_extcon_cables);
|
||||
|
@ -441,7 +466,9 @@ static int axp288_extcon_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
/* Start charger cable type detection */
|
||||
axp288_extcon_enable(info);
|
||||
ret = axp288_extcon_enable(info);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
device_init_wakeup(dev, true);
|
||||
platform_set_drvdata(pdev, info);
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
*/
|
||||
|
||||
#include <linux/extcon-provider.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
|
|
|
@ -7,18 +7,17 @@
|
|||
*/
|
||||
|
||||
#include <linux/extcon-provider.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
|
||||
#define USB_GPIO_DEBOUNCE_MS 20 /* ms */
|
||||
|
||||
|
|
|
@ -19,15 +19,42 @@
|
|||
#define TUSB320_REG9_ATTACHED_STATE_MASK 0x3
|
||||
#define TUSB320_REG9_CABLE_DIRECTION BIT(5)
|
||||
#define TUSB320_REG9_INTERRUPT_STATUS BIT(4)
|
||||
#define TUSB320_ATTACHED_STATE_NONE 0x0
|
||||
#define TUSB320_ATTACHED_STATE_DFP 0x1
|
||||
#define TUSB320_ATTACHED_STATE_UFP 0x2
|
||||
#define TUSB320_ATTACHED_STATE_ACC 0x3
|
||||
|
||||
#define TUSB320_REGA 0xa
|
||||
#define TUSB320L_REGA_DISABLE_TERM BIT(0)
|
||||
#define TUSB320_REGA_I2C_SOFT_RESET BIT(3)
|
||||
#define TUSB320_REGA_MODE_SELECT_SHIFT 4
|
||||
#define TUSB320_REGA_MODE_SELECT_MASK 0x3
|
||||
|
||||
#define TUSB320L_REGA0_REVISION 0xa0
|
||||
|
||||
enum tusb320_attached_state {
|
||||
TUSB320_ATTACHED_STATE_NONE,
|
||||
TUSB320_ATTACHED_STATE_DFP,
|
||||
TUSB320_ATTACHED_STATE_UFP,
|
||||
TUSB320_ATTACHED_STATE_ACC,
|
||||
};
|
||||
|
||||
enum tusb320_mode {
|
||||
TUSB320_MODE_PORT,
|
||||
TUSB320_MODE_UFP,
|
||||
TUSB320_MODE_DFP,
|
||||
TUSB320_MODE_DRP,
|
||||
};
|
||||
|
||||
struct tusb320_priv;
|
||||
|
||||
struct tusb320_ops {
|
||||
int (*set_mode)(struct tusb320_priv *priv, enum tusb320_mode mode);
|
||||
int (*get_revision)(struct tusb320_priv *priv, unsigned int *revision);
|
||||
};
|
||||
|
||||
struct tusb320_priv {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct extcon_dev *edev;
|
||||
struct tusb320_ops *ops;
|
||||
enum tusb320_attached_state state;
|
||||
};
|
||||
|
||||
static const char * const tusb_attached_states[] = {
|
||||
|
@ -62,6 +89,101 @@ static int tusb320_check_signature(struct tusb320_priv *priv)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int tusb320_set_mode(struct tusb320_priv *priv, enum tusb320_mode mode)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Mode cannot be changed while cable is attached */
|
||||
if (priv->state != TUSB320_ATTACHED_STATE_NONE)
|
||||
return -EBUSY;
|
||||
|
||||
/* Write mode */
|
||||
ret = regmap_write_bits(priv->regmap, TUSB320_REGA,
|
||||
TUSB320_REGA_MODE_SELECT_MASK << TUSB320_REGA_MODE_SELECT_SHIFT,
|
||||
mode << TUSB320_REGA_MODE_SELECT_SHIFT);
|
||||
if (ret) {
|
||||
dev_err(priv->dev, "failed to write mode: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tusb320l_set_mode(struct tusb320_priv *priv, enum tusb320_mode mode)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Disable CC state machine */
|
||||
ret = regmap_write_bits(priv->regmap, TUSB320_REGA,
|
||||
TUSB320L_REGA_DISABLE_TERM, 1);
|
||||
if (ret) {
|
||||
dev_err(priv->dev,
|
||||
"failed to disable CC state machine: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Write mode */
|
||||
ret = regmap_write_bits(priv->regmap, TUSB320_REGA,
|
||||
TUSB320_REGA_MODE_SELECT_MASK << TUSB320_REGA_MODE_SELECT_SHIFT,
|
||||
mode << TUSB320_REGA_MODE_SELECT_SHIFT);
|
||||
if (ret) {
|
||||
dev_err(priv->dev, "failed to write mode: %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
msleep(5);
|
||||
err:
|
||||
/* Re-enable CC state machine */
|
||||
ret = regmap_write_bits(priv->regmap, TUSB320_REGA,
|
||||
TUSB320L_REGA_DISABLE_TERM, 0);
|
||||
if (ret)
|
||||
dev_err(priv->dev,
|
||||
"failed to re-enable CC state machine: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tusb320_reset(struct tusb320_priv *priv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Set mode to default (follow PORT pin) */
|
||||
ret = priv->ops->set_mode(priv, TUSB320_MODE_PORT);
|
||||
if (ret && ret != -EBUSY) {
|
||||
dev_err(priv->dev,
|
||||
"failed to set mode to PORT: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Perform soft reset */
|
||||
ret = regmap_write_bits(priv->regmap, TUSB320_REGA,
|
||||
TUSB320_REGA_I2C_SOFT_RESET, 1);
|
||||
if (ret) {
|
||||
dev_err(priv->dev,
|
||||
"failed to write soft reset bit: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Wait for chip to go through reset */
|
||||
msleep(95);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tusb320l_get_revision(struct tusb320_priv *priv, unsigned int *revision)
|
||||
{
|
||||
return regmap_read(priv->regmap, TUSB320L_REGA0_REVISION, revision);
|
||||
}
|
||||
|
||||
static struct tusb320_ops tusb320_ops = {
|
||||
.set_mode = tusb320_set_mode,
|
||||
};
|
||||
|
||||
static struct tusb320_ops tusb320l_ops = {
|
||||
.set_mode = tusb320l_set_mode,
|
||||
.get_revision = tusb320l_get_revision,
|
||||
};
|
||||
|
||||
static irqreturn_t tusb320_irq_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct tusb320_priv *priv = dev_id;
|
||||
|
@ -96,6 +218,8 @@ static irqreturn_t tusb320_irq_handler(int irq, void *dev_id)
|
|||
extcon_sync(priv->edev, EXTCON_USB);
|
||||
extcon_sync(priv->edev, EXTCON_USB_HOST);
|
||||
|
||||
priv->state = state;
|
||||
|
||||
regmap_write(priv->regmap, TUSB320_REG9, reg);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
|
@ -110,6 +234,8 @@ static int tusb320_extcon_probe(struct i2c_client *client,
|
|||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct tusb320_priv *priv;
|
||||
const void *match_data;
|
||||
unsigned int revision;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
|
||||
|
@ -125,12 +251,27 @@ static int tusb320_extcon_probe(struct i2c_client *client,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
match_data = device_get_match_data(&client->dev);
|
||||
if (!match_data)
|
||||
return -EINVAL;
|
||||
|
||||
priv->ops = (struct tusb320_ops*)match_data;
|
||||
|
||||
priv->edev = devm_extcon_dev_allocate(priv->dev, tusb320_extcon_cable);
|
||||
if (IS_ERR(priv->edev)) {
|
||||
dev_err(priv->dev, "failed to allocate extcon device\n");
|
||||
return PTR_ERR(priv->edev);
|
||||
}
|
||||
|
||||
if (priv->ops->get_revision) {
|
||||
ret = priv->ops->get_revision(priv, &revision);
|
||||
if (ret)
|
||||
dev_warn(priv->dev,
|
||||
"failed to read revision register: %d\n", ret);
|
||||
else
|
||||
dev_info(priv->dev, "chip revision %d\n", revision);
|
||||
}
|
||||
|
||||
ret = devm_extcon_dev_register(priv->dev, priv->edev);
|
||||
if (ret < 0) {
|
||||
dev_err(priv->dev, "failed to register extcon device\n");
|
||||
|
@ -145,6 +286,17 @@ static int tusb320_extcon_probe(struct i2c_client *client,
|
|||
/* update initial state */
|
||||
tusb320_irq_handler(client->irq, priv);
|
||||
|
||||
/* Reset chip to its default state */
|
||||
ret = tusb320_reset(priv);
|
||||
if (ret)
|
||||
dev_warn(priv->dev, "failed to reset chip: %d\n", ret);
|
||||
else
|
||||
/*
|
||||
* State and polarity might change after a reset, so update
|
||||
* them again and make sure the interrupt status bit is cleared.
|
||||
*/
|
||||
tusb320_irq_handler(client->irq, priv);
|
||||
|
||||
ret = devm_request_threaded_irq(priv->dev, client->irq, NULL,
|
||||
tusb320_irq_handler,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
|
@ -154,7 +306,8 @@ static int tusb320_extcon_probe(struct i2c_client *client,
|
|||
}
|
||||
|
||||
static const struct of_device_id tusb320_extcon_dt_match[] = {
|
||||
{ .compatible = "ti,tusb320", },
|
||||
{ .compatible = "ti,tusb320", .data = &tusb320_ops, },
|
||||
{ .compatible = "ti,tusb320l", .data = &tusb320l_ops, },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tusb320_extcon_dt_match);
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/fsi-occ.h>
|
||||
|
@ -33,13 +34,6 @@
|
|||
|
||||
#define OCC_P10_SRAM_MODE 0x58 /* Normal mode, OCB channel 2 */
|
||||
|
||||
/*
|
||||
* Assume we don't have much FFDC, if we do we'll overflow and
|
||||
* fail the command. This needs to be big enough for simple
|
||||
* commands as well.
|
||||
*/
|
||||
#define OCC_SBE_STATUS_WORDS 32
|
||||
|
||||
#define OCC_TIMEOUT_MS 1000
|
||||
#define OCC_CMD_IN_PRG_WAIT_MS 50
|
||||
|
||||
|
@ -50,6 +44,11 @@ struct occ {
|
|||
struct device *sbefifo;
|
||||
char name[32];
|
||||
int idx;
|
||||
u8 sequence_number;
|
||||
void *buffer;
|
||||
void *client_buffer;
|
||||
size_t client_buffer_size;
|
||||
size_t client_response_size;
|
||||
enum versions version;
|
||||
struct miscdevice mdev;
|
||||
struct mutex occ_lock;
|
||||
|
@ -141,8 +140,7 @@ static ssize_t occ_write(struct file *file, const char __user *buf,
|
|||
{
|
||||
struct occ_client *client = file->private_data;
|
||||
size_t rlen, data_length;
|
||||
u16 checksum = 0;
|
||||
ssize_t rc, i;
|
||||
ssize_t rc;
|
||||
u8 *cmd;
|
||||
|
||||
if (!client)
|
||||
|
@ -156,9 +154,6 @@ static ssize_t occ_write(struct file *file, const char __user *buf,
|
|||
/* Construct the command */
|
||||
cmd = client->buffer;
|
||||
|
||||
/* Sequence number (we could increment and compare with response) */
|
||||
cmd[0] = 1;
|
||||
|
||||
/*
|
||||
* Copy the user command (assume user data follows the occ command
|
||||
* format)
|
||||
|
@ -178,14 +173,7 @@ static ssize_t occ_write(struct file *file, const char __user *buf,
|
|||
goto done;
|
||||
}
|
||||
|
||||
/* Calculate checksum */
|
||||
for (i = 0; i < data_length + 4; ++i)
|
||||
checksum += cmd[i];
|
||||
|
||||
cmd[data_length + 4] = checksum >> 8;
|
||||
cmd[data_length + 5] = checksum & 0xFF;
|
||||
|
||||
/* Submit command */
|
||||
/* Submit command; 4 bytes before the data and 2 bytes after */
|
||||
rlen = PAGE_SIZE;
|
||||
rc = fsi_occ_submit(client->occ->dev, cmd, data_length + 6, cmd,
|
||||
&rlen);
|
||||
|
@ -223,6 +211,22 @@ static const struct file_operations occ_fops = {
|
|||
.release = occ_release,
|
||||
};
|
||||
|
||||
static void occ_save_ffdc(struct occ *occ, __be32 *resp, size_t parsed_len,
|
||||
size_t resp_len)
|
||||
{
|
||||
if (resp_len > parsed_len) {
|
||||
size_t dh = resp_len - parsed_len;
|
||||
size_t ffdc_len = (dh - 1) * 4; /* SBE words are four bytes */
|
||||
__be32 *ffdc = &resp[parsed_len];
|
||||
|
||||
if (ffdc_len > occ->client_buffer_size)
|
||||
ffdc_len = occ->client_buffer_size;
|
||||
|
||||
memcpy(occ->client_buffer, ffdc, ffdc_len);
|
||||
occ->client_response_size = ffdc_len;
|
||||
}
|
||||
}
|
||||
|
||||
static int occ_verify_checksum(struct occ *occ, struct occ_response *resp,
|
||||
u16 data_length)
|
||||
{
|
||||
|
@ -251,8 +255,10 @@ static int occ_verify_checksum(struct occ *occ, struct occ_response *resp,
|
|||
static int occ_getsram(struct occ *occ, u32 offset, void *data, ssize_t len)
|
||||
{
|
||||
u32 data_len = ((len + 7) / 8) * 8; /* must be multiples of 8 B */
|
||||
size_t cmd_len, resp_len, resp_data_len;
|
||||
__be32 *resp, cmd[6];
|
||||
size_t cmd_len, parsed_len, resp_data_len;
|
||||
size_t resp_len = OCC_MAX_RESP_WORDS;
|
||||
__be32 *resp = occ->buffer;
|
||||
__be32 cmd[6];
|
||||
int idx = 0, rc;
|
||||
|
||||
/*
|
||||
|
@ -279,21 +285,22 @@ static int occ_getsram(struct occ *occ, u32 offset, void *data, ssize_t len)
|
|||
cmd[1] = cpu_to_be32(SBEFIFO_CMD_GET_OCC_SRAM);
|
||||
cmd[4 + idx] = cpu_to_be32(data_len);
|
||||
|
||||
resp_len = (data_len >> 2) + OCC_SBE_STATUS_WORDS;
|
||||
resp = kzalloc(resp_len << 2, GFP_KERNEL);
|
||||
if (!resp)
|
||||
return -ENOMEM;
|
||||
|
||||
rc = sbefifo_submit(occ->sbefifo, cmd, cmd_len, resp, &resp_len);
|
||||
if (rc)
|
||||
goto free;
|
||||
return rc;
|
||||
|
||||
rc = sbefifo_parse_status(occ->sbefifo, SBEFIFO_CMD_GET_OCC_SRAM,
|
||||
resp, resp_len, &resp_len);
|
||||
if (rc)
|
||||
goto free;
|
||||
resp, resp_len, &parsed_len);
|
||||
if (rc > 0) {
|
||||
dev_err(occ->dev, "SRAM read returned failure status: %08x\n",
|
||||
rc);
|
||||
occ_save_ffdc(occ, resp, parsed_len, resp_len);
|
||||
return -ECOMM;
|
||||
} else if (rc) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
resp_data_len = be32_to_cpu(resp[resp_len - 1]);
|
||||
resp_data_len = be32_to_cpu(resp[parsed_len - 1]);
|
||||
if (resp_data_len != data_len) {
|
||||
dev_err(occ->dev, "SRAM read expected %d bytes got %zd\n",
|
||||
data_len, resp_data_len);
|
||||
|
@ -302,37 +309,21 @@ static int occ_getsram(struct occ *occ, u32 offset, void *data, ssize_t len)
|
|||
memcpy(data, resp, len);
|
||||
}
|
||||
|
||||
free:
|
||||
/* Convert positive SBEI status */
|
||||
if (rc > 0) {
|
||||
dev_err(occ->dev, "SRAM read returned failure status: %08x\n",
|
||||
rc);
|
||||
rc = -EBADMSG;
|
||||
}
|
||||
|
||||
kfree(resp);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int occ_putsram(struct occ *occ, const void *data, ssize_t len)
|
||||
static int occ_putsram(struct occ *occ, const void *data, ssize_t len,
|
||||
u8 seq_no, u16 checksum)
|
||||
{
|
||||
size_t cmd_len, buf_len, resp_len, resp_data_len;
|
||||
u32 data_len = ((len + 7) / 8) * 8; /* must be multiples of 8 B */
|
||||
__be32 *buf;
|
||||
size_t cmd_len, parsed_len, resp_data_len;
|
||||
size_t resp_len = OCC_MAX_RESP_WORDS;
|
||||
__be32 *buf = occ->buffer;
|
||||
u8 *byte_buf;
|
||||
int idx = 0, rc;
|
||||
|
||||
cmd_len = (occ->version == occ_p10) ? 6 : 5;
|
||||
|
||||
/*
|
||||
* We use the same buffer for command and response, make
|
||||
* sure it's big enough
|
||||
*/
|
||||
resp_len = OCC_SBE_STATUS_WORDS;
|
||||
cmd_len += data_len >> 2;
|
||||
buf_len = max(cmd_len, resp_len);
|
||||
buf = kzalloc(buf_len << 2, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Magic sequence to do SBE putsram command. SBE will transfer
|
||||
|
@ -358,18 +349,33 @@ static int occ_putsram(struct occ *occ, const void *data, ssize_t len)
|
|||
buf[4 + idx] = cpu_to_be32(data_len);
|
||||
memcpy(&buf[5 + idx], data, len);
|
||||
|
||||
byte_buf = (u8 *)&buf[5 + idx];
|
||||
/*
|
||||
* Overwrite the first byte with our sequence number and the last two
|
||||
* bytes with the checksum.
|
||||
*/
|
||||
byte_buf[0] = seq_no;
|
||||
byte_buf[len - 2] = checksum >> 8;
|
||||
byte_buf[len - 1] = checksum & 0xff;
|
||||
|
||||
rc = sbefifo_submit(occ->sbefifo, buf, cmd_len, buf, &resp_len);
|
||||
if (rc)
|
||||
goto free;
|
||||
return rc;
|
||||
|
||||
rc = sbefifo_parse_status(occ->sbefifo, SBEFIFO_CMD_PUT_OCC_SRAM,
|
||||
buf, resp_len, &resp_len);
|
||||
if (rc)
|
||||
goto free;
|
||||
buf, resp_len, &parsed_len);
|
||||
if (rc > 0) {
|
||||
dev_err(occ->dev, "SRAM write returned failure status: %08x\n",
|
||||
rc);
|
||||
occ_save_ffdc(occ, buf, parsed_len, resp_len);
|
||||
return -ECOMM;
|
||||
} else if (rc) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (resp_len != 1) {
|
||||
if (parsed_len != 1) {
|
||||
dev_err(occ->dev, "SRAM write response length invalid: %zd\n",
|
||||
resp_len);
|
||||
parsed_len);
|
||||
rc = -EBADMSG;
|
||||
} else {
|
||||
resp_data_len = be32_to_cpu(buf[0]);
|
||||
|
@ -381,27 +387,16 @@ static int occ_putsram(struct occ *occ, const void *data, ssize_t len)
|
|||
}
|
||||
}
|
||||
|
||||
free:
|
||||
/* Convert positive SBEI status */
|
||||
if (rc > 0) {
|
||||
dev_err(occ->dev, "SRAM write returned failure status: %08x\n",
|
||||
rc);
|
||||
rc = -EBADMSG;
|
||||
}
|
||||
|
||||
kfree(buf);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int occ_trigger_attn(struct occ *occ)
|
||||
{
|
||||
__be32 buf[OCC_SBE_STATUS_WORDS];
|
||||
size_t cmd_len, resp_len, resp_data_len;
|
||||
__be32 *buf = occ->buffer;
|
||||
size_t cmd_len, parsed_len, resp_data_len;
|
||||
size_t resp_len = OCC_MAX_RESP_WORDS;
|
||||
int idx = 0, rc;
|
||||
|
||||
BUILD_BUG_ON(OCC_SBE_STATUS_WORDS < 8);
|
||||
resp_len = OCC_SBE_STATUS_WORDS;
|
||||
|
||||
switch (occ->version) {
|
||||
default:
|
||||
case occ_p9:
|
||||
|
@ -426,16 +421,22 @@ static int occ_trigger_attn(struct occ *occ)
|
|||
|
||||
rc = sbefifo_submit(occ->sbefifo, buf, cmd_len, buf, &resp_len);
|
||||
if (rc)
|
||||
goto error;
|
||||
return rc;
|
||||
|
||||
rc = sbefifo_parse_status(occ->sbefifo, SBEFIFO_CMD_PUT_OCC_SRAM,
|
||||
buf, resp_len, &resp_len);
|
||||
if (rc)
|
||||
goto error;
|
||||
buf, resp_len, &parsed_len);
|
||||
if (rc > 0) {
|
||||
dev_err(occ->dev, "SRAM attn returned failure status: %08x\n",
|
||||
rc);
|
||||
occ_save_ffdc(occ, buf, parsed_len, resp_len);
|
||||
return -ECOMM;
|
||||
} else if (rc) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (resp_len != 1) {
|
||||
if (parsed_len != 1) {
|
||||
dev_err(occ->dev, "SRAM attn response length invalid: %zd\n",
|
||||
resp_len);
|
||||
parsed_len);
|
||||
rc = -EBADMSG;
|
||||
} else {
|
||||
resp_data_len = be32_to_cpu(buf[0]);
|
||||
|
@ -447,14 +448,6 @@ static int occ_trigger_attn(struct occ *occ)
|
|||
}
|
||||
}
|
||||
|
||||
error:
|
||||
/* Convert positive SBEI status */
|
||||
if (rc > 0) {
|
||||
dev_err(occ->dev, "SRAM attn returned failure status: %08x\n",
|
||||
rc);
|
||||
rc = -EBADMSG;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -466,24 +459,49 @@ int fsi_occ_submit(struct device *dev, const void *request, size_t req_len,
|
|||
msecs_to_jiffies(OCC_CMD_IN_PRG_WAIT_MS);
|
||||
struct occ *occ = dev_get_drvdata(dev);
|
||||
struct occ_response *resp = response;
|
||||
size_t user_resp_len = *resp_len;
|
||||
u8 seq_no;
|
||||
u16 checksum = 0;
|
||||
u16 resp_data_length;
|
||||
const u8 *byte_request = (const u8 *)request;
|
||||
unsigned long start;
|
||||
int rc;
|
||||
size_t i;
|
||||
|
||||
*resp_len = 0;
|
||||
|
||||
if (!occ)
|
||||
return -ENODEV;
|
||||
|
||||
if (*resp_len < 7) {
|
||||
dev_dbg(dev, "Bad resplen %zd\n", *resp_len);
|
||||
if (user_resp_len < 7) {
|
||||
dev_dbg(dev, "Bad resplen %zd\n", user_resp_len);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Checksum the request, ignoring first byte (sequence number). */
|
||||
for (i = 1; i < req_len - 2; ++i)
|
||||
checksum += byte_request[i];
|
||||
|
||||
mutex_lock(&occ->occ_lock);
|
||||
|
||||
/* Extract the seq_no from the command (first byte) */
|
||||
seq_no = *(const u8 *)request;
|
||||
rc = occ_putsram(occ, request, req_len);
|
||||
occ->client_buffer = response;
|
||||
occ->client_buffer_size = user_resp_len;
|
||||
occ->client_response_size = 0;
|
||||
|
||||
/*
|
||||
* Get a sequence number and update the counter. Avoid a sequence
|
||||
* number of 0 which would pass the response check below even if the
|
||||
* OCC response is uninitialized. Any sequence number the user is
|
||||
* trying to send is overwritten since this function is the only common
|
||||
* interface to the OCC and therefore the only place we can guarantee
|
||||
* unique sequence numbers.
|
||||
*/
|
||||
seq_no = occ->sequence_number++;
|
||||
if (!occ->sequence_number)
|
||||
occ->sequence_number = 1;
|
||||
checksum += seq_no;
|
||||
|
||||
rc = occ_putsram(occ, request, req_len, seq_no, checksum);
|
||||
if (rc)
|
||||
goto done;
|
||||
|
||||
|
@ -520,7 +538,7 @@ int fsi_occ_submit(struct device *dev, const void *request, size_t req_len,
|
|||
resp_data_length = get_unaligned_be16(&resp->data_length);
|
||||
|
||||
/* Message size is data length + 5 bytes header + 2 bytes checksum */
|
||||
if ((resp_data_length + 7) > *resp_len) {
|
||||
if ((resp_data_length + 7) > user_resp_len) {
|
||||
rc = -EMSGSIZE;
|
||||
goto done;
|
||||
}
|
||||
|
@ -536,10 +554,11 @@ int fsi_occ_submit(struct device *dev, const void *request, size_t req_len,
|
|||
goto done;
|
||||
}
|
||||
|
||||
*resp_len = resp_data_length + 7;
|
||||
occ->client_response_size = resp_data_length + 7;
|
||||
rc = occ_verify_checksum(occ, resp, resp_data_length);
|
||||
|
||||
done:
|
||||
*resp_len = occ->client_response_size;
|
||||
mutex_unlock(&occ->occ_lock);
|
||||
|
||||
return rc;
|
||||
|
@ -571,9 +590,15 @@ static int occ_probe(struct platform_device *pdev)
|
|||
if (!occ)
|
||||
return -ENOMEM;
|
||||
|
||||
/* SBE words are always four bytes */
|
||||
occ->buffer = kvmalloc(OCC_MAX_RESP_WORDS * 4, GFP_KERNEL);
|
||||
if (!occ->buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
occ->version = (uintptr_t)of_device_get_match_data(dev);
|
||||
occ->dev = dev;
|
||||
occ->sbefifo = dev->parent;
|
||||
occ->sequence_number = 1;
|
||||
mutex_init(&occ->occ_lock);
|
||||
|
||||
if (dev->of_node) {
|
||||
|
@ -605,6 +630,7 @@ static int occ_probe(struct platform_device *pdev)
|
|||
if (rc) {
|
||||
dev_err(dev, "failed to register miscdevice: %d\n", rc);
|
||||
ida_simple_remove(&occ_ida, occ->idx);
|
||||
kvfree(occ->buffer);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -620,6 +646,8 @@ static int occ_remove(struct platform_device *pdev)
|
|||
{
|
||||
struct occ *occ = platform_get_drvdata(pdev);
|
||||
|
||||
kvfree(occ->buffer);
|
||||
|
||||
misc_deregister(&occ->mdev);
|
||||
|
||||
device_for_each_child(&pdev->dev, NULL, occ_unregister_child);
|
||||
|
|
|
@ -124,6 +124,7 @@ struct sbefifo {
|
|||
bool broken;
|
||||
bool dead;
|
||||
bool async_ffdc;
|
||||
bool timed_out;
|
||||
};
|
||||
|
||||
struct sbefifo_user {
|
||||
|
@ -136,6 +137,14 @@ struct sbefifo_user {
|
|||
|
||||
static DEFINE_MUTEX(sbefifo_ffdc_mutex);
|
||||
|
||||
static ssize_t timeout_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct sbefifo *sbefifo = container_of(dev, struct sbefifo, dev);
|
||||
|
||||
return sysfs_emit(buf, "%d\n", sbefifo->timed_out ? 1 : 0);
|
||||
}
|
||||
static DEVICE_ATTR_RO(timeout);
|
||||
|
||||
static void __sbefifo_dump_ffdc(struct device *dev, const __be32 *ffdc,
|
||||
size_t ffdc_sz, bool internal)
|
||||
|
@ -462,11 +471,14 @@ static int sbefifo_wait(struct sbefifo *sbefifo, bool up,
|
|||
break;
|
||||
}
|
||||
if (!ready) {
|
||||
sysfs_notify(&sbefifo->dev.kobj, NULL, dev_attr_timeout.attr.name);
|
||||
sbefifo->timed_out = true;
|
||||
dev_err(dev, "%s FIFO Timeout ! status=%08x\n", up ? "UP" : "DOWN", sts);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
dev_vdbg(dev, "End of wait status: %08x\n", sts);
|
||||
|
||||
sbefifo->timed_out = false;
|
||||
*status = sts;
|
||||
|
||||
return 0;
|
||||
|
@ -740,7 +752,9 @@ int sbefifo_submit(struct device *dev, const __be32 *command, size_t cmd_len,
|
|||
iov_iter_kvec(&resp_iter, WRITE, &resp_iov, 1, rbytes);
|
||||
|
||||
/* Perform the command */
|
||||
mutex_lock(&sbefifo->lock);
|
||||
rc = mutex_lock_interruptible(&sbefifo->lock);
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = __sbefifo_submit(sbefifo, command, cmd_len, &resp_iter);
|
||||
mutex_unlock(&sbefifo->lock);
|
||||
|
||||
|
@ -820,7 +834,9 @@ static ssize_t sbefifo_user_read(struct file *file, char __user *buf,
|
|||
iov_iter_init(&resp_iter, WRITE, &resp_iov, 1, len);
|
||||
|
||||
/* Perform the command */
|
||||
mutex_lock(&sbefifo->lock);
|
||||
rc = mutex_lock_interruptible(&sbefifo->lock);
|
||||
if (rc)
|
||||
goto bail;
|
||||
rc = __sbefifo_submit(sbefifo, user->pending_cmd, cmd_len, &resp_iter);
|
||||
mutex_unlock(&sbefifo->lock);
|
||||
if (rc < 0)
|
||||
|
@ -875,7 +891,9 @@ static ssize_t sbefifo_user_write(struct file *file, const char __user *buf,
|
|||
user->pending_len = 0;
|
||||
|
||||
/* Trigger reset request */
|
||||
mutex_lock(&sbefifo->lock);
|
||||
rc = mutex_lock_interruptible(&sbefifo->lock);
|
||||
if (rc)
|
||||
goto bail;
|
||||
rc = sbefifo_request_reset(user->sbefifo);
|
||||
mutex_unlock(&sbefifo->lock);
|
||||
if (rc == 0)
|
||||
|
@ -993,6 +1011,8 @@ static int sbefifo_probe(struct device *dev)
|
|||
child_name);
|
||||
}
|
||||
|
||||
device_create_file(&sbefifo->dev, &dev_attr_timeout);
|
||||
|
||||
return 0;
|
||||
err_free_minor:
|
||||
fsi_free_minor(sbefifo->dev.devt);
|
||||
|
@ -1018,6 +1038,8 @@ static int sbefifo_remove(struct device *dev)
|
|||
|
||||
dev_dbg(dev, "Removing sbefifo device...\n");
|
||||
|
||||
device_remove_file(&sbefifo->dev, &dev_attr_timeout);
|
||||
|
||||
mutex_lock(&sbefifo->lock);
|
||||
sbefifo->dead = true;
|
||||
mutex_unlock(&sbefifo->lock);
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include <linux/swiotlb.h>
|
||||
#include <linux/dma-buf.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <drm/ttm/ttm_bo_api.h>
|
||||
#include <drm/ttm/ttm_bo_driver.h>
|
||||
|
@ -59,6 +60,8 @@
|
|||
#include "amdgpu_res_cursor.h"
|
||||
#include "bif/bif_4_1_d.h"
|
||||
|
||||
MODULE_IMPORT_NS(DMA_BUF);
|
||||
|
||||
#define AMDGPU_TTM_VRAM_MAX_DW_READ (size_t)128
|
||||
|
||||
static int amdgpu_ttm_backend_bind(struct ttm_device *bdev,
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
#include "armada_gem.h"
|
||||
#include "armada_ioctlP.h"
|
||||
|
||||
MODULE_IMPORT_NS(DMA_BUF);
|
||||
|
||||
static vm_fault_t armada_gem_vm_fault(struct vm_fault *vmf)
|
||||
{
|
||||
struct drm_gem_object *gobj = vmf->vma->vm_private_data;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <drm/drm_damage_helper.h>
|
||||
#include <drm/drm_fb_helper.h>
|
||||
|
@ -17,6 +18,8 @@
|
|||
|
||||
#include "drm_internal.h"
|
||||
|
||||
MODULE_IMPORT_NS(DMA_BUF);
|
||||
|
||||
#define AFBC_HEADER_SIZE 16
|
||||
#define AFBC_TH_LAYOUT_ALIGNMENT 8
|
||||
#define AFBC_HDR_ALIGN 64
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
#include <drm/drm_prime.h>
|
||||
#include <drm/drm_print.h>
|
||||
|
||||
MODULE_IMPORT_NS(DMA_BUF);
|
||||
|
||||
/**
|
||||
* DOC: overview
|
||||
*
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include <linux/export.h>
|
||||
#include <linux/dma-buf.h>
|
||||
#include <linux/rbtree.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <drm/drm.h>
|
||||
#include <drm/drm_drv.h>
|
||||
|
@ -39,6 +40,8 @@
|
|||
|
||||
#include "drm_internal.h"
|
||||
|
||||
MODULE_IMPORT_NS(DMA_BUF);
|
||||
|
||||
/**
|
||||
* DOC: overview and lifetime rules
|
||||
*
|
||||
|
|
|
@ -5,10 +5,13 @@
|
|||
|
||||
#include <drm/drm_prime.h>
|
||||
#include <linux/dma-buf.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "etnaviv_drv.h"
|
||||
#include "etnaviv_gem.h"
|
||||
|
||||
MODULE_IMPORT_NS(DMA_BUF);
|
||||
|
||||
static struct lock_class_key etnaviv_prime_lock_class;
|
||||
|
||||
struct sg_table *etnaviv_gem_prime_get_sg_table(struct drm_gem_object *obj)
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <linux/dma-buf.h>
|
||||
#include <linux/pfn_t.h>
|
||||
#include <linux/shmem_fs.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <drm/drm_prime.h>
|
||||
#include <drm/drm_vma_manager.h>
|
||||
|
@ -17,6 +18,8 @@
|
|||
#include "exynos_drm_drv.h"
|
||||
#include "exynos_drm_gem.h"
|
||||
|
||||
MODULE_IMPORT_NS(DMA_BUF);
|
||||
|
||||
static int exynos_drm_alloc_buf(struct exynos_drm_gem *exynos_gem, bool kvmap)
|
||||
{
|
||||
struct drm_device *dev = exynos_gem->base.dev;
|
||||
|
|
|
@ -7,11 +7,14 @@
|
|||
#include <linux/dma-buf.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/dma-resv.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "i915_drv.h"
|
||||
#include "i915_gem_object.h"
|
||||
#include "i915_scatterlist.h"
|
||||
|
||||
MODULE_IMPORT_NS(DMA_BUF);
|
||||
|
||||
I915_SELFTEST_DECLARE(static bool force_different_devices;)
|
||||
|
||||
static struct drm_i915_gem_object *dma_buf_to_obj(struct dma_buf *buf)
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#include "omap_drv.h"
|
||||
|
||||
MODULE_IMPORT_NS(DMA_BUF);
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* DMABUF Export
|
||||
*/
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include <linux/dma-buf.h>
|
||||
#include <linux/iommu.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drm_prime.h>
|
||||
|
@ -20,6 +21,8 @@
|
|||
#include "drm.h"
|
||||
#include "gem.h"
|
||||
|
||||
MODULE_IMPORT_NS(DMA_BUF);
|
||||
|
||||
static void tegra_bo_put(struct host1x_bo *bo)
|
||||
{
|
||||
struct tegra_bo *obj = host1x_to_tegra_bo(bo);
|
||||
|
|
|
@ -48,8 +48,11 @@
|
|||
#include <linux/spinlock.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/module.h>
|
||||
#include "ttm_object.h"
|
||||
|
||||
MODULE_IMPORT_NS(DMA_BUF);
|
||||
|
||||
/**
|
||||
* struct ttm_object_file
|
||||
*
|
||||
|
|
|
@ -132,22 +132,20 @@ struct extended_sensor {
|
|||
static int occ_poll(struct occ *occ)
|
||||
{
|
||||
int rc;
|
||||
u16 checksum = occ->poll_cmd_data + occ->seq_no + 1;
|
||||
u8 cmd[8];
|
||||
u8 cmd[7];
|
||||
struct occ_poll_response_header *header;
|
||||
|
||||
/* big endian */
|
||||
cmd[0] = occ->seq_no++; /* sequence number */
|
||||
cmd[0] = 0; /* sequence number */
|
||||
cmd[1] = 0; /* cmd type */
|
||||
cmd[2] = 0; /* data length msb */
|
||||
cmd[3] = 1; /* data length lsb */
|
||||
cmd[4] = occ->poll_cmd_data; /* data */
|
||||
cmd[5] = checksum >> 8; /* checksum msb */
|
||||
cmd[6] = checksum & 0xFF; /* checksum lsb */
|
||||
cmd[7] = 0;
|
||||
cmd[5] = 0; /* checksum msb */
|
||||
cmd[6] = 0; /* checksum lsb */
|
||||
|
||||
/* mutex should already be locked if necessary */
|
||||
rc = occ->send_cmd(occ, cmd);
|
||||
rc = occ->send_cmd(occ, cmd, sizeof(cmd));
|
||||
if (rc) {
|
||||
occ->last_error = rc;
|
||||
if (occ->error_count++ > OCC_ERROR_COUNT_THRESHOLD)
|
||||
|
@ -184,25 +182,23 @@ static int occ_set_user_power_cap(struct occ *occ, u16 user_power_cap)
|
|||
{
|
||||
int rc;
|
||||
u8 cmd[8];
|
||||
u16 checksum = 0x24;
|
||||
__be16 user_power_cap_be = cpu_to_be16(user_power_cap);
|
||||
|
||||
cmd[0] = 0;
|
||||
cmd[1] = 0x22;
|
||||
cmd[2] = 0;
|
||||
cmd[3] = 2;
|
||||
cmd[0] = 0; /* sequence number */
|
||||
cmd[1] = 0x22; /* cmd type */
|
||||
cmd[2] = 0; /* data length msb */
|
||||
cmd[3] = 2; /* data length lsb */
|
||||
|
||||
memcpy(&cmd[4], &user_power_cap_be, 2);
|
||||
|
||||
checksum += cmd[4] + cmd[5];
|
||||
cmd[6] = checksum >> 8;
|
||||
cmd[7] = checksum & 0xFF;
|
||||
cmd[6] = 0; /* checksum msb */
|
||||
cmd[7] = 0; /* checksum lsb */
|
||||
|
||||
rc = mutex_lock_interruptible(&occ->lock);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = occ->send_cmd(occ, cmd);
|
||||
rc = occ->send_cmd(occ, cmd, sizeof(cmd));
|
||||
|
||||
mutex_unlock(&occ->lock);
|
||||
|
||||
|
@ -1144,8 +1140,6 @@ int occ_setup(struct occ *occ, const char *name)
|
|||
{
|
||||
int rc;
|
||||
|
||||
/* start with 1 to avoid false match with zero-initialized SRAM buffer */
|
||||
occ->seq_no = 1;
|
||||
mutex_init(&occ->lock);
|
||||
occ->groups[0] = &occ->group;
|
||||
|
||||
|
|
|
@ -95,9 +95,8 @@ struct occ {
|
|||
struct occ_sensors sensors;
|
||||
|
||||
int powr_sample_time_us; /* average power sample time */
|
||||
u8 seq_no;
|
||||
u8 poll_cmd_data; /* to perform OCC poll command */
|
||||
int (*send_cmd)(struct occ *occ, u8 *cmd);
|
||||
int (*send_cmd)(struct occ *occ, u8 *cmd, size_t len);
|
||||
|
||||
unsigned long next_update;
|
||||
struct mutex lock; /* lock OCC access */
|
||||
|
|
|
@ -97,18 +97,21 @@ static int p8_i2c_occ_putscom_u32(struct i2c_client *client, u32 address,
|
|||
}
|
||||
|
||||
static int p8_i2c_occ_putscom_be(struct i2c_client *client, u32 address,
|
||||
u8 *data)
|
||||
u8 *data, size_t len)
|
||||
{
|
||||
__be32 data0, data1;
|
||||
__be32 data0 = 0, data1 = 0;
|
||||
|
||||
memcpy(&data0, data, 4);
|
||||
memcpy(&data1, data + 4, 4);
|
||||
memcpy(&data0, data, min_t(size_t, len, 4));
|
||||
if (len > 4) {
|
||||
len -= 4;
|
||||
memcpy(&data1, data + 4, min_t(size_t, len, 4));
|
||||
}
|
||||
|
||||
return p8_i2c_occ_putscom_u32(client, address, be32_to_cpu(data0),
|
||||
be32_to_cpu(data1));
|
||||
}
|
||||
|
||||
static int p8_i2c_occ_send_cmd(struct occ *occ, u8 *cmd)
|
||||
static int p8_i2c_occ_send_cmd(struct occ *occ, u8 *cmd, size_t len)
|
||||
{
|
||||
int i, rc;
|
||||
unsigned long start;
|
||||
|
@ -127,7 +130,7 @@ static int p8_i2c_occ_send_cmd(struct occ *occ, u8 *cmd)
|
|||
return rc;
|
||||
|
||||
/* write command (expected to already be BE), we need bus-endian... */
|
||||
rc = p8_i2c_occ_putscom_be(client, OCB_DATA3, cmd);
|
||||
rc = p8_i2c_occ_putscom_be(client, OCB_DATA3, cmd, len);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
|
|
|
@ -4,28 +4,96 @@
|
|||
#include <linux/device.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fsi-occ.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/sysfs.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
struct p9_sbe_occ {
|
||||
struct occ occ;
|
||||
bool sbe_error;
|
||||
void *ffdc;
|
||||
size_t ffdc_len;
|
||||
size_t ffdc_size;
|
||||
struct mutex sbe_error_lock; /* lock access to ffdc data */
|
||||
struct device *sbe;
|
||||
};
|
||||
|
||||
#define to_p9_sbe_occ(x) container_of((x), struct p9_sbe_occ, occ)
|
||||
|
||||
static int p9_sbe_occ_send_cmd(struct occ *occ, u8 *cmd)
|
||||
static ssize_t ffdc_read(struct file *filp, struct kobject *kobj,
|
||||
struct bin_attribute *battr, char *buf, loff_t pos,
|
||||
size_t count)
|
||||
{
|
||||
ssize_t rc = 0;
|
||||
struct occ *occ = dev_get_drvdata(kobj_to_dev(kobj));
|
||||
struct p9_sbe_occ *ctx = to_p9_sbe_occ(occ);
|
||||
|
||||
mutex_lock(&ctx->sbe_error_lock);
|
||||
if (ctx->sbe_error) {
|
||||
rc = memory_read_from_buffer(buf, count, &pos, ctx->ffdc,
|
||||
ctx->ffdc_len);
|
||||
if (pos >= ctx->ffdc_len)
|
||||
ctx->sbe_error = false;
|
||||
}
|
||||
mutex_unlock(&ctx->sbe_error_lock);
|
||||
|
||||
return rc;
|
||||
}
|
||||
static BIN_ATTR_RO(ffdc, OCC_MAX_RESP_WORDS * 4);
|
||||
|
||||
static bool p9_sbe_occ_save_ffdc(struct p9_sbe_occ *ctx, const void *resp,
|
||||
size_t resp_len)
|
||||
{
|
||||
bool notify = false;
|
||||
|
||||
mutex_lock(&ctx->sbe_error_lock);
|
||||
if (!ctx->sbe_error) {
|
||||
if (resp_len > ctx->ffdc_size) {
|
||||
if (ctx->ffdc)
|
||||
kvfree(ctx->ffdc);
|
||||
ctx->ffdc = kvmalloc(resp_len, GFP_KERNEL);
|
||||
if (!ctx->ffdc) {
|
||||
ctx->ffdc_len = 0;
|
||||
ctx->ffdc_size = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ctx->ffdc_size = resp_len;
|
||||
}
|
||||
|
||||
notify = true;
|
||||
ctx->sbe_error = true;
|
||||
ctx->ffdc_len = resp_len;
|
||||
memcpy(ctx->ffdc, resp, resp_len);
|
||||
}
|
||||
|
||||
done:
|
||||
mutex_unlock(&ctx->sbe_error_lock);
|
||||
return notify;
|
||||
}
|
||||
|
||||
static int p9_sbe_occ_send_cmd(struct occ *occ, u8 *cmd, size_t len)
|
||||
{
|
||||
struct occ_response *resp = &occ->resp;
|
||||
struct p9_sbe_occ *ctx = to_p9_sbe_occ(occ);
|
||||
size_t resp_len = sizeof(*resp);
|
||||
int rc;
|
||||
|
||||
rc = fsi_occ_submit(ctx->sbe, cmd, 8, resp, &resp_len);
|
||||
if (rc < 0)
|
||||
rc = fsi_occ_submit(ctx->sbe, cmd, len, resp, &resp_len);
|
||||
if (rc < 0) {
|
||||
if (resp_len) {
|
||||
if (p9_sbe_occ_save_ffdc(ctx, resp, resp_len))
|
||||
sysfs_notify(&occ->bus_dev->kobj, NULL,
|
||||
bin_attr_ffdc.attr.name);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
switch (resp->return_status) {
|
||||
case OCC_RESP_CMD_IN_PRG:
|
||||
|
@ -65,6 +133,8 @@ static int p9_sbe_occ_probe(struct platform_device *pdev)
|
|||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&ctx->sbe_error_lock);
|
||||
|
||||
ctx->sbe = pdev->dev.parent;
|
||||
occ = &ctx->occ;
|
||||
occ->bus_dev = &pdev->dev;
|
||||
|
@ -78,6 +148,15 @@ static int p9_sbe_occ_probe(struct platform_device *pdev)
|
|||
if (rc == -ESHUTDOWN)
|
||||
rc = -ENODEV; /* Host is shutdown, don't spew errors */
|
||||
|
||||
if (!rc) {
|
||||
rc = device_create_bin_file(occ->bus_dev, &bin_attr_ffdc);
|
||||
if (rc) {
|
||||
dev_warn(occ->bus_dev,
|
||||
"failed to create SBE error ffdc file\n");
|
||||
rc = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -86,9 +165,14 @@ static int p9_sbe_occ_remove(struct platform_device *pdev)
|
|||
struct occ *occ = platform_get_drvdata(pdev);
|
||||
struct p9_sbe_occ *ctx = to_p9_sbe_occ(occ);
|
||||
|
||||
device_remove_bin_file(occ->bus_dev, &bin_attr_ffdc);
|
||||
|
||||
ctx->sbe = NULL;
|
||||
occ_shutdown(occ);
|
||||
|
||||
if (ctx->ffdc)
|
||||
kvfree(ctx->ffdc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -150,6 +150,19 @@ config CORESIGHT_CPU_DEBUG
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called coresight-cpu-debug.
|
||||
|
||||
config CORESIGHT_CPU_DEBUG_DEFAULT_ON
|
||||
bool "Enable CoreSight CPU Debug by default"
|
||||
depends on CORESIGHT_CPU_DEBUG
|
||||
help
|
||||
Say Y here to enable the CoreSight Debug panic-debug by default. This
|
||||
can also be enabled via debugfs, but this ensures the debug feature
|
||||
is enabled as early as possible.
|
||||
|
||||
Has the same effect as setting coresight_cpu_debug.enable=1 on the
|
||||
kernel command line.
|
||||
|
||||
Say N if unsure.
|
||||
|
||||
config CORESIGHT_CTI
|
||||
tristate "CoreSight Cross Trigger Interface (CTI) driver"
|
||||
depends on ARM || ARM64
|
||||
|
|
|
@ -105,7 +105,7 @@ static DEFINE_PER_CPU(struct debug_drvdata *, debug_drvdata);
|
|||
static int debug_count;
|
||||
static struct dentry *debug_debugfs_dir;
|
||||
|
||||
static bool debug_enable;
|
||||
static bool debug_enable = IS_ENABLED(CONFIG_CORESIGHT_CPU_DEBUG_DEFAULT_ON);
|
||||
module_param_named(enable, debug_enable, bool, 0600);
|
||||
MODULE_PARM_DESC(enable, "Control to enable coresight CPU debug functionality");
|
||||
|
||||
|
|
|
@ -175,7 +175,7 @@ static int cti_disable_hw(struct cti_drvdata *drvdata)
|
|||
coresight_disclaim_device_unlocked(csdev);
|
||||
CS_LOCK(drvdata->base);
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
pm_runtime_put(dev);
|
||||
pm_runtime_put(dev->parent);
|
||||
return 0;
|
||||
|
||||
/* not disabled this call */
|
||||
|
|
|
@ -557,9 +557,8 @@ static unsigned long etb_update_buffer(struct coresight_device *csdev,
|
|||
|
||||
/*
|
||||
* In snapshot mode we simply increment the head by the number of byte
|
||||
* that were written. User space function cs_etm_find_snapshot() will
|
||||
* figure out how many bytes to get from the AUX buffer based on the
|
||||
* position of the head.
|
||||
* that were written. User space will figure out how many bytes to get
|
||||
* from the AUX buffer based on the position of the head.
|
||||
*/
|
||||
if (buf->snapshot)
|
||||
handle->head += to_read;
|
||||
|
|
|
@ -452,9 +452,14 @@ static void etm_event_start(struct perf_event *event, int flags)
|
|||
* sink from this ETM. We can't do much in this case if
|
||||
* the sink was specified or hinted to the driver. For
|
||||
* now, simply don't record anything on this ETM.
|
||||
*
|
||||
* As such we pretend that everything is fine, and let
|
||||
* it continue without actually tracing. The event could
|
||||
* continue tracing when it moves to a CPU where it is
|
||||
* reachable to a sink.
|
||||
*/
|
||||
if (!cpumask_test_cpu(cpu, &event_data->mask))
|
||||
goto fail_end_stop;
|
||||
goto out;
|
||||
|
||||
path = etm_event_cpu_path(event_data, cpu);
|
||||
/* We need a sink, no need to continue without one */
|
||||
|
@ -466,26 +471,32 @@ static void etm_event_start(struct perf_event *event, int flags)
|
|||
if (coresight_enable_path(path, CS_MODE_PERF, handle))
|
||||
goto fail_end_stop;
|
||||
|
||||
/* Tell the perf core the event is alive */
|
||||
event->hw.state = 0;
|
||||
|
||||
/* Finally enable the tracer */
|
||||
if (source_ops(csdev)->enable(csdev, event, CS_MODE_PERF))
|
||||
goto fail_disable_path;
|
||||
|
||||
out:
|
||||
/* Tell the perf core the event is alive */
|
||||
event->hw.state = 0;
|
||||
/* Save the event_data for this ETM */
|
||||
ctxt->event_data = event_data;
|
||||
out:
|
||||
return;
|
||||
|
||||
fail_disable_path:
|
||||
coresight_disable_path(path);
|
||||
fail_end_stop:
|
||||
perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED);
|
||||
perf_aux_output_end(handle, 0);
|
||||
/*
|
||||
* Check if the handle is still associated with the event,
|
||||
* to handle cases where if the sink failed to start the
|
||||
* trace and TRUNCATED the handle already.
|
||||
*/
|
||||
if (READ_ONCE(handle->event)) {
|
||||
perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED);
|
||||
perf_aux_output_end(handle, 0);
|
||||
}
|
||||
fail:
|
||||
event->hw.state = PERF_HES_STOPPED;
|
||||
goto out;
|
||||
return;
|
||||
}
|
||||
|
||||
static void etm_event_stop(struct perf_event *event, int mode)
|
||||
|
@ -517,6 +528,19 @@ static void etm_event_stop(struct perf_event *event, int mode)
|
|||
if (WARN_ON(!event_data))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Check if this ETM was allowed to trace, as decided at
|
||||
* etm_setup_aux(). If it wasn't allowed to trace, then
|
||||
* nothing needs to be torn down other than outputting a
|
||||
* zero sized record.
|
||||
*/
|
||||
if (handle->event && (mode & PERF_EF_UPDATE) &&
|
||||
!cpumask_test_cpu(cpu, &event_data->mask)) {
|
||||
event->hw.state = PERF_HES_STOPPED;
|
||||
perf_aux_output_end(handle, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!csdev)
|
||||
return;
|
||||
|
||||
|
@ -550,7 +574,21 @@ static void etm_event_stop(struct perf_event *event, int mode)
|
|||
|
||||
size = sink_ops(sink)->update_buffer(sink, handle,
|
||||
event_data->snk_config);
|
||||
perf_aux_output_end(handle, size);
|
||||
/*
|
||||
* Make sure the handle is still valid as the
|
||||
* sink could have closed it from an IRQ.
|
||||
* The sink driver must handle the race with
|
||||
* update_buffer() and IRQ. Thus either we
|
||||
* should get a valid handle and valid size
|
||||
* (which may be 0).
|
||||
*
|
||||
* But we should never get a non-zero size with
|
||||
* an invalid handle.
|
||||
*/
|
||||
if (READ_ONCE(handle->event))
|
||||
perf_aux_output_end(handle, size);
|
||||
else
|
||||
WARN_ON(size);
|
||||
}
|
||||
|
||||
/* Disabling the path make its elements available to other sessions */
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
#include "coresight-etm4x.h"
|
||||
#include "coresight-etm-perf.h"
|
||||
#include "coresight-etm4x-cfg.h"
|
||||
#include "coresight-self-hosted-trace.h"
|
||||
#include "coresight-syscfg.h"
|
||||
|
||||
static int boot_enable;
|
||||
|
@ -238,6 +239,45 @@ struct etm4_enable_arg {
|
|||
int rc;
|
||||
};
|
||||
|
||||
/*
|
||||
* etm4x_prohibit_trace - Prohibit the CPU from tracing at all ELs.
|
||||
* When the CPU supports FEAT_TRF, we could move the ETM to a trace
|
||||
* prohibited state by filtering the Exception levels via TRFCR_EL1.
|
||||
*/
|
||||
static void etm4x_prohibit_trace(struct etmv4_drvdata *drvdata)
|
||||
{
|
||||
/* If the CPU doesn't support FEAT_TRF, nothing to do */
|
||||
if (!drvdata->trfcr)
|
||||
return;
|
||||
cpu_prohibit_trace();
|
||||
}
|
||||
|
||||
/*
|
||||
* etm4x_allow_trace - Allow CPU tracing in the respective ELs,
|
||||
* as configured by the drvdata->config.mode for the current
|
||||
* session. Even though we have TRCVICTLR bits to filter the
|
||||
* trace in the ELs, it doesn't prevent the ETM from generating
|
||||
* a packet (e.g, TraceInfo) that might contain the addresses from
|
||||
* the excluded levels. Thus we use the additional controls provided
|
||||
* via the Trace Filtering controls (FEAT_TRF) to make sure no trace
|
||||
* is generated for the excluded ELs.
|
||||
*/
|
||||
static void etm4x_allow_trace(struct etmv4_drvdata *drvdata)
|
||||
{
|
||||
u64 trfcr = drvdata->trfcr;
|
||||
|
||||
/* If the CPU doesn't support FEAT_TRF, nothing to do */
|
||||
if (!trfcr)
|
||||
return;
|
||||
|
||||
if (drvdata->config.mode & ETM_MODE_EXCL_KERN)
|
||||
trfcr &= ~TRFCR_ELx_ExTRE;
|
||||
if (drvdata->config.mode & ETM_MODE_EXCL_USER)
|
||||
trfcr &= ~TRFCR_ELx_E0TRE;
|
||||
|
||||
write_trfcr(trfcr);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ETM4X_IMPDEF_FEATURE
|
||||
|
||||
#define HISI_HIP08_AMBA_ID 0x000b6d01
|
||||
|
@ -442,6 +482,7 @@ static int etm4_enable_hw(struct etmv4_drvdata *drvdata)
|
|||
if (etm4x_is_ete(drvdata))
|
||||
etm4x_relaxed_write32(csa, TRCRSR_TA, TRCRSR);
|
||||
|
||||
etm4x_allow_trace(drvdata);
|
||||
/* Enable the trace unit */
|
||||
etm4x_relaxed_write32(csa, 1, TRCPRGCTLR);
|
||||
|
||||
|
@ -737,7 +778,6 @@ static int etm4_enable(struct coresight_device *csdev,
|
|||
static void etm4_disable_hw(void *info)
|
||||
{
|
||||
u32 control;
|
||||
u64 trfcr;
|
||||
struct etmv4_drvdata *drvdata = info;
|
||||
struct etmv4_config *config = &drvdata->config;
|
||||
struct coresight_device *csdev = drvdata->csdev;
|
||||
|
@ -764,12 +804,7 @@ static void etm4_disable_hw(void *info)
|
|||
* If the CPU supports v8.4 Trace filter Control,
|
||||
* set the ETM to trace prohibited region.
|
||||
*/
|
||||
if (drvdata->trfc) {
|
||||
trfcr = read_sysreg_s(SYS_TRFCR_EL1);
|
||||
write_sysreg_s(trfcr & ~(TRFCR_ELx_ExTRE | TRFCR_ELx_E0TRE),
|
||||
SYS_TRFCR_EL1);
|
||||
isb();
|
||||
}
|
||||
etm4x_prohibit_trace(drvdata);
|
||||
/*
|
||||
* Make sure everything completes before disabling, as recommended
|
||||
* by section 7.3.77 ("TRCVICTLR, ViewInst Main Control Register,
|
||||
|
@ -785,9 +820,6 @@ static void etm4_disable_hw(void *info)
|
|||
if (coresight_timeout(csa, TRCSTATR, TRCSTATR_PMSTABLE_BIT, 1))
|
||||
dev_err(etm_dev,
|
||||
"timeout while waiting for PM stable Trace Status\n");
|
||||
if (drvdata->trfc)
|
||||
write_sysreg_s(trfcr, SYS_TRFCR_EL1);
|
||||
|
||||
/* read the status of the single shot comparators */
|
||||
for (i = 0; i < drvdata->nr_ss_cmp; i++) {
|
||||
config->ss_status[i] =
|
||||
|
@ -989,15 +1021,15 @@ static bool etm4_init_csdev_access(struct etmv4_drvdata *drvdata,
|
|||
return false;
|
||||
}
|
||||
|
||||
static void cpu_enable_tracing(struct etmv4_drvdata *drvdata)
|
||||
static void cpu_detect_trace_filtering(struct etmv4_drvdata *drvdata)
|
||||
{
|
||||
u64 dfr0 = read_sysreg(id_aa64dfr0_el1);
|
||||
u64 trfcr;
|
||||
|
||||
drvdata->trfcr = 0;
|
||||
if (!cpuid_feature_extract_unsigned_field(dfr0, ID_AA64DFR0_TRACE_FILT_SHIFT))
|
||||
return;
|
||||
|
||||
drvdata->trfc = true;
|
||||
/*
|
||||
* If the CPU supports v8.4 SelfHosted Tracing, enable
|
||||
* tracing at the kernel EL and EL0, forcing to use the
|
||||
|
@ -1011,7 +1043,7 @@ static void cpu_enable_tracing(struct etmv4_drvdata *drvdata)
|
|||
if (is_kernel_in_hyp_mode())
|
||||
trfcr |= TRFCR_EL2_CX;
|
||||
|
||||
write_sysreg_s(trfcr, SYS_TRFCR_EL1);
|
||||
drvdata->trfcr = trfcr;
|
||||
}
|
||||
|
||||
static void etm4_init_arch_data(void *info)
|
||||
|
@ -1202,7 +1234,7 @@ static void etm4_init_arch_data(void *info)
|
|||
/* NUMCNTR, bits[30:28] number of counters available for tracing */
|
||||
drvdata->nr_cntr = BMVAL(etmidr5, 28, 30);
|
||||
etm4_cs_lock(drvdata, csa);
|
||||
cpu_enable_tracing(drvdata);
|
||||
cpu_detect_trace_filtering(drvdata);
|
||||
}
|
||||
|
||||
static inline u32 etm4_get_victlr_access_type(struct etmv4_config *config)
|
||||
|
@ -1554,7 +1586,7 @@ static void etm4_init_trace_id(struct etmv4_drvdata *drvdata)
|
|||
drvdata->trcid = coresight_get_trace_id(drvdata->cpu);
|
||||
}
|
||||
|
||||
static int etm4_cpu_save(struct etmv4_drvdata *drvdata)
|
||||
static int __etm4_cpu_save(struct etmv4_drvdata *drvdata)
|
||||
{
|
||||
int i, ret = 0;
|
||||
struct etmv4_save_state *state;
|
||||
|
@ -1693,7 +1725,23 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void etm4_cpu_restore(struct etmv4_drvdata *drvdata)
|
||||
static int etm4_cpu_save(struct etmv4_drvdata *drvdata)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/* Save the TRFCR irrespective of whether the ETM is ON */
|
||||
if (drvdata->trfcr)
|
||||
drvdata->save_trfcr = read_trfcr();
|
||||
/*
|
||||
* Save and restore the ETM Trace registers only if
|
||||
* the ETM is active.
|
||||
*/
|
||||
if (local_read(&drvdata->mode) && drvdata->save_state)
|
||||
ret = __etm4_cpu_save(drvdata);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __etm4_cpu_restore(struct etmv4_drvdata *drvdata)
|
||||
{
|
||||
int i;
|
||||
struct etmv4_save_state *state = drvdata->save_state;
|
||||
|
@ -1789,6 +1837,14 @@ static void etm4_cpu_restore(struct etmv4_drvdata *drvdata)
|
|||
etm4_cs_lock(drvdata, csa);
|
||||
}
|
||||
|
||||
static void etm4_cpu_restore(struct etmv4_drvdata *drvdata)
|
||||
{
|
||||
if (drvdata->trfcr)
|
||||
write_trfcr(drvdata->save_trfcr);
|
||||
if (drvdata->state_needs_restore)
|
||||
__etm4_cpu_restore(drvdata);
|
||||
}
|
||||
|
||||
static int etm4_cpu_pm_notify(struct notifier_block *nb, unsigned long cmd,
|
||||
void *v)
|
||||
{
|
||||
|
@ -1800,23 +1856,17 @@ static int etm4_cpu_pm_notify(struct notifier_block *nb, unsigned long cmd,
|
|||
|
||||
drvdata = etmdrvdata[cpu];
|
||||
|
||||
if (!drvdata->save_state)
|
||||
return NOTIFY_OK;
|
||||
|
||||
if (WARN_ON_ONCE(drvdata->cpu != cpu))
|
||||
return NOTIFY_BAD;
|
||||
|
||||
switch (cmd) {
|
||||
case CPU_PM_ENTER:
|
||||
/* save the state if self-hosted coresight is in use */
|
||||
if (local_read(&drvdata->mode))
|
||||
if (etm4_cpu_save(drvdata))
|
||||
return NOTIFY_BAD;
|
||||
if (etm4_cpu_save(drvdata))
|
||||
return NOTIFY_BAD;
|
||||
break;
|
||||
case CPU_PM_EXIT:
|
||||
case CPU_PM_ENTER_FAILED:
|
||||
if (drvdata->state_needs_restore)
|
||||
etm4_cpu_restore(drvdata);
|
||||
etm4_cpu_restore(drvdata);
|
||||
break;
|
||||
default:
|
||||
return NOTIFY_DONE;
|
||||
|
@ -2099,6 +2149,7 @@ static const struct amba_id etm4_ids[] = {
|
|||
CS_AMBA_UCI_ID(0x000bb803, uci_id_etm4),/* Qualcomm Kryo 385 Cortex-A75 */
|
||||
CS_AMBA_UCI_ID(0x000bb805, uci_id_etm4),/* Qualcomm Kryo 4XX Cortex-A55 */
|
||||
CS_AMBA_UCI_ID(0x000bb804, uci_id_etm4),/* Qualcomm Kryo 4XX Cortex-A76 */
|
||||
CS_AMBA_UCI_ID(0x000bbd0d, uci_id_etm4),/* Qualcomm Kryo 5XX Cortex-A77 */
|
||||
CS_AMBA_UCI_ID(0x000cc0af, uci_id_etm4),/* Marvell ThunderX2 */
|
||||
CS_AMBA_UCI_ID(0x000b6d01, uci_id_etm4),/* HiSilicon-Hip08 */
|
||||
CS_AMBA_UCI_ID(0x000b6d02, uci_id_etm4),/* HiSilicon-Hip09 */
|
||||
|
|
|
@ -919,8 +919,12 @@ struct etmv4_save_state {
|
|||
* @nooverflow: Indicate if overflow prevention is supported.
|
||||
* @atbtrig: If the implementation can support ATB triggers
|
||||
* @lpoverride: If the implementation can support low-power state over.
|
||||
* @trfc: If the implementation supports Arm v8.4 trace filter controls.
|
||||
* @trfcr: If the CPU supports FEAT_TRF, value of the TRFCR_ELx that
|
||||
* allows tracing at all ELs. We don't want to compute this
|
||||
* at runtime, due to the additional setting of TRFCR_CX when
|
||||
* in EL2. Otherwise, 0.
|
||||
* @config: structure holding configuration parameters.
|
||||
* @save_trfcr: Saved TRFCR_EL1 register during a CPU PM event.
|
||||
* @save_state: State to be preserved across power loss
|
||||
* @state_needs_restore: True when there is context to restore after PM exit
|
||||
* @skip_power_up: Indicates if an implementation can skip powering up
|
||||
|
@ -971,8 +975,9 @@ struct etmv4_drvdata {
|
|||
bool nooverflow;
|
||||
bool atbtrig;
|
||||
bool lpoverride;
|
||||
bool trfc;
|
||||
u64 trfcr;
|
||||
struct etmv4_config config;
|
||||
u64 save_trfcr;
|
||||
struct etmv4_save_state *save_state;
|
||||
bool state_needs_restore;
|
||||
bool skip_power_up;
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Arm v8 Self-Hosted trace support.
|
||||
*
|
||||
* Copyright (C) 2021 ARM Ltd.
|
||||
*/
|
||||
|
||||
#ifndef __CORESIGHT_SELF_HOSTED_TRACE_H
|
||||
#define __CORESIGHT_SELF_HOSTED_TRACE_H
|
||||
|
||||
#include <asm/sysreg.h>
|
||||
|
||||
static inline u64 read_trfcr(void)
|
||||
{
|
||||
return read_sysreg_s(SYS_TRFCR_EL1);
|
||||
}
|
||||
|
||||
static inline void write_trfcr(u64 val)
|
||||
{
|
||||
write_sysreg_s(val, SYS_TRFCR_EL1);
|
||||
isb();
|
||||
}
|
||||
|
||||
static inline u64 cpu_prohibit_trace(void)
|
||||
{
|
||||
u64 trfcr = read_trfcr();
|
||||
|
||||
/* Prohibit tracing at EL0 & the kernel EL */
|
||||
write_trfcr(trfcr & ~(TRFCR_ELx_ExTRE | TRFCR_ELx_E0TRE));
|
||||
/* Return the original value of the TRFCR */
|
||||
return trfcr;
|
||||
}
|
||||
#endif /* __CORESIGHT_SELF_HOSTED_TRACE_H */
|
|
@ -432,6 +432,21 @@ static u32 tmc_etr_get_default_buffer_size(struct device *dev)
|
|||
return size;
|
||||
}
|
||||
|
||||
static u32 tmc_etr_get_max_burst_size(struct device *dev)
|
||||
{
|
||||
u32 burst_size;
|
||||
|
||||
if (fwnode_property_read_u32(dev->fwnode, "arm,max-burst-size",
|
||||
&burst_size))
|
||||
return TMC_AXICTL_WR_BURST_16;
|
||||
|
||||
/* Only permissible values are 0 to 15 */
|
||||
if (burst_size > 0xF)
|
||||
burst_size = TMC_AXICTL_WR_BURST_16;
|
||||
|
||||
return burst_size;
|
||||
}
|
||||
|
||||
static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
{
|
||||
int ret = 0;
|
||||
|
@ -469,10 +484,12 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
|
|||
/* This device is not associated with a session */
|
||||
drvdata->pid = -1;
|
||||
|
||||
if (drvdata->config_type == TMC_CONFIG_TYPE_ETR)
|
||||
if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
|
||||
drvdata->size = tmc_etr_get_default_buffer_size(dev);
|
||||
else
|
||||
drvdata->max_burst_size = tmc_etr_get_max_burst_size(dev);
|
||||
} else {
|
||||
drvdata->size = readl_relaxed(drvdata->base + TMC_RSZ) * 4;
|
||||
}
|
||||
|
||||
desc.dev = dev;
|
||||
desc.groups = coresight_tmc_groups;
|
||||
|
|
|
@ -546,13 +546,17 @@ static unsigned long tmc_update_etf_buffer(struct coresight_device *csdev,
|
|||
|
||||
/*
|
||||
* In snapshot mode we simply increment the head by the number of byte
|
||||
* that were written. User space function cs_etm_find_snapshot() will
|
||||
* figure out how many bytes to get from the AUX buffer based on the
|
||||
* position of the head.
|
||||
* that were written. User space will figure out how many bytes to get
|
||||
* from the AUX buffer based on the position of the head.
|
||||
*/
|
||||
if (buf->snapshot)
|
||||
handle->head += to_read;
|
||||
|
||||
/*
|
||||
* CS_LOCK() contains mb() so it can ensure visibility of the AUX trace
|
||||
* data before the aux_head is updated via perf_aux_output_end(), which
|
||||
* is expected by the perf ring buffer.
|
||||
*/
|
||||
CS_LOCK(drvdata->base);
|
||||
out:
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче