Merge 1c9f8dff62 ("Merge tag 'char-misc-6.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc") into android-mainline

Steps on the way to 6.6-rc1

Change-Id: I1dfff264b71cfa9757f52ebb65b065de2c7a56b4
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
This commit is contained in:
Greg Kroah-Hartman
2023-10-17 17:41:22 +00:00
350 changed files with 21224 additions and 3156 deletions
+2 -2
View File
@@ -1,7 +1,7 @@
What: /sys/bus/mhi/devices/.../serialnumber
Date: Sept 2020
KernelVersion: 5.10
Contact: Bhaumik Bhatt <bbhatt@codeaurora.org>
Contact: mhi@lists.linux.dev
Description: The file holds the serial number of the client device obtained
using a BHI (Boot Host Interface) register read after at least
one attempt to power up the device has been done. If read
@@ -12,7 +12,7 @@ Users: Any userspace application or clients interested in device info.
What: /sys/bus/mhi/devices/.../oem_pk_hash
Date: Sept 2020
KernelVersion: 5.10
Contact: Bhaumik Bhatt <bbhatt@codeaurora.org>
Contact: mhi@lists.linux.dev
Description: The file holds the OEM PK Hash value of the endpoint device
obtained using a BHI (Boot Host Interface) register read after
at least one attempt to power up the device has been done. If
+4 -4
View File
@@ -22,11 +22,11 @@ Description:
phase clock.
What: /sys/bus/counter/devices/counterX/external_input_phase_clock_select_available
KernelVersion: 6.4
Contact: linux-iio@vger.kernel.org
KernelVersion: 6.4
Contact: linux-iio@vger.kernel.org
Description:
Discrete set of available values for the respective device
configuration are listed in this file.
Discrete set of available values for the respective device
configuration are listed in this file.
What: /sys/bus/counter/devices/counterX/countY/count
KernelVersion: 5.2
@@ -7,4 +7,4 @@ Description:
by the driver. A value of 1 indicates that a timeout has
occurred and no transfers have completed since the timeout. A
value of 0 indicates that no timeout has occurred, or if one
has, more recent transfers have completed successful.
has, more recent transfers have completed successfully.
+16
View File
@@ -2163,3 +2163,19 @@ Contact: linux-iio@vger.kernel.org
Description:
An example format is 16-bytes, 2-digits-per-byte, HEX-string
representing the sensor unique ID number.
What: /sys/.../events/in_proximity_thresh_either_runningperiod
KernelVersion: 6.6
Contact: linux-iio@vger.kernel.org
Description:
A running period of time (in seconds) for which
in_proximity_thresh_either_runningcount amount of conditions
must occur before an event is generated. If direction is not
specified then this period applies to both directions.
What: /sys/.../events/in_proximity_thresh_either_runningcount
KernelVersion: 6.6
Contact: linux-iio@vger.kernel.org
Description:
Number of conditions that must occur, during a running
period, before an event is generated.
@@ -7,6 +7,8 @@ Description:
- auto -> Adjust bandpass filter to track changes in input clock rate.
- manual -> disable/unregister the clock rate notifier / input clock tracking.
- bypass -> bypass low pass filter, high pass filter and disable/unregister
the clock rate notifier
What: /sys/bus/iio/devices/iio:deviceX/filter_mode
KernelVersion:
@@ -216,13 +216,14 @@ The flags are::
t Include thread ID, or <intr>
m Include module name
f Include the function name
s Include the source file name
l Include line number
For ``print_hex_dump_debug()`` and ``print_hex_dump_bytes()``, only
the ``p`` flag has meaning, other flags are ignored.
Note the regexp ``^[-+=][flmpt_]+$`` matches a flags specification.
To clear all flags at once, use ``=_`` or ``-flmpt``.
Note the regexp ``^[-+=][fslmpt_]+$`` matches a flags specification.
To clear all flags at once, use ``=_`` or ``-fslmpt``.
Debug messages during Boot Process
@@ -23,6 +23,7 @@ properties:
connector:
$ref: /schemas/connector/usb-connector.yaml#
unevaluatedProperties: false
ports:
$ref: /schemas/graph.yaml#/properties/ports
@@ -27,6 +27,10 @@ properties:
description: I2C slave address of the device. Usually 0x25 for SM5502
and SM5703, 0x14 for SM5504.
connector:
$ref: /schemas/connector/usb-connector.yaml#
unevaluatedProperties: false
interrupts:
maxItems: 1
@@ -0,0 +1,41 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/fsi/ibm,i2cr-fsi-master.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: IBM I2C Responder virtual FSI master
maintainers:
- Eddie James <eajames@linux.ibm.com>
description: |
The I2C Responder (I2CR) is a an I2C device that's connected to an FSI CFAM
(see fsi.txt). The I2CR translates I2C bus operations to FSI CFAM reads and
writes or SCOM operations, thereby acting as an FSI master.
properties:
compatible:
enum:
- ibm,i2cr-fsi-master
reg:
maxItems: 1
required:
- compatible
- reg
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
i2cr@20 {
compatible = "ibm,i2cr-fsi-master";
reg = <0x20>;
};
};
@@ -0,0 +1,91 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/adc/allwinner,sun20i-d1-gpadc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Allwinner D1 General Purpose ADC
maintainers:
- Maksim Kiselev <bigunclemax@gmail.com>
properties:
compatible:
enum:
- allwinner,sun20i-d1-gpadc
"#io-channel-cells":
const: 1
"#address-cells":
const: 1
"#size-cells":
const: 0
clocks:
maxItems: 1
interrupts:
maxItems: 1
reg:
maxItems: 1
resets:
maxItems: 1
patternProperties:
"^channel@[0-9a-f]+$":
$ref: adc.yaml
type: object
description:
Represents the internal channels of the ADC.
properties:
reg:
items:
minimum: 0
maximum: 15
required:
- reg
unevaluatedProperties: false
required:
- "#io-channel-cells"
- clocks
- compatible
- interrupts
- reg
- resets
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/sun20i-d1-ccu.h>
#include <dt-bindings/reset/sun20i-d1-ccu.h>
#include <dt-bindings/interrupt-controller/irq.h>
gpadc: adc@2009000 {
compatible = "allwinner,sun20i-d1-gpadc";
reg = <0x2009000 0x400>;
clocks = <&ccu CLK_BUS_GPADC>;
resets = <&ccu RST_BUS_GPADC>;
interrupts = <73 IRQ_TYPE_LEVEL_HIGH>;
#io-channel-cells = <1>;
#address-cells = <1>;
#size-cells = <0>;
channel@0 {
reg = <0>;
};
channel@1 {
reg = <1>;
};
};
...
@@ -78,9 +78,9 @@ patternProperties:
ti,datarate:
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 6
maximum: 7
description: |
Data acquisition rate in samples per second
Data acquisition rate in samples per second for ADS1015, TLA2024
0: 128
1: 250
2: 490
@@ -88,6 +88,17 @@ patternProperties:
4: 1600 (default)
5: 2400
6: 3300
7: 3300
Data acquisition rate in samples per second for ADS1115
0: 8
1: 16
2: 32
3: 64
4: 128 (default)
5: 250
6: 475
7: 860
required:
- reg
@@ -0,0 +1,49 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/dac/microchip,mcp4728.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Microchip MCP4728 DAC
maintainers:
- Andrea Collamati <andrea.collamati@gmail.com>
description: |
MCP4728 is a quad channel, 12-bit voltage output
Digital-to-Analog Converter with non-volatile
memory and I2C compatible Serial Interface.
https://www.microchip.com/en-us/product/mcp4728
properties:
compatible:
const: microchip,mcp4728
reg:
maxItems: 1
vdd-supply:
description: |
Provides both power and acts as the reference supply on the MCP4728
when Internal Vref is not selected.
required:
- compatible
- reg
- vdd-supply
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
dac@60 {
compatible = "microchip,mcp4728";
reg = <0x60>;
vdd-supply = <&vdac_vdd>;
};
};
@@ -39,6 +39,46 @@ properties:
description:
Analog voltage regulator.
vcc-drv-supply:
description:
RF Driver voltage regulator.
vcc2-drv-supply:
description:
RF predriver voltage regulator.
vcc-vva-supply:
description:
VVA Control Circuit voltage regulator.
vcc-amp1-supply:
description:
RF Amplifier 1 voltage regulator.
vcc-amp2-supply:
description:
RF Amplifier 2 voltage regulator.
vcc-env-supply:
description:
Envelope Detector voltage regulator.
vcc-bg-supply:
description:
Mixer Chip Band Gap Circuit voltage regulator.
vcc-bg2-supply:
description:
VGA Chip Band Gap Circuit voltage regulator.
vcc-mixer-supply:
description:
Mixer voltage regulator.
vcc-quad-supply:
description:
Quadruppler voltage regulator.
adi,detector-enable:
description:
Enable the Envelope Detector available at output pins VENV_P and
@@ -69,6 +109,16 @@ required:
- clocks
- clock-names
- vcm-supply
- vcc-drv-supply
- vcc2-drv-supply
- vcc-vva-supply
- vcc-amp1-supply
- vcc-amp2-supply
- vcc-env-supply
- vcc-bg-supply
- vcc-bg2-supply
- vcc-mixer-supply
- vcc-quad-supply
allOf:
- $ref: /schemas/spi/spi-peripheral-props.yaml#
@@ -87,6 +137,16 @@ examples:
clocks = <&admv1013_lo>;
clock-names = "lo_in";
vcm-supply = <&vcm>;
vcc-drv-supply = <&vcc_drv>;
vcc2-drv-supply = <&vcc2_drv>;
vcc-vva-supply = <&vcc_vva>;
vcc-amp1-supply = <&vcc_amp1>;
vcc-amp2-supply = <&vcc_amp2>;
vcc-env-supply = <&vcc_env>;
vcc-bg-supply = <&vcc_bg>;
vcc-bg2-supply = <&vcc_bg2>;
vcc-mixer-supply = <&vcc_mixer>;
vcc-quad-supply = <&vcc_quad>;
adi,quad-se-mode = "diff";
adi,detector-enable;
};
@@ -103,6 +103,14 @@ required:
- clocks
- clock-names
- vcm-supply
- vcc-if-bb-supply
- vcc-vga-supply
- vcc-vva-supply
- vcc-lna-3p3-supply
- vcc-lna-1p5-supply
- vcc-bg-supply
- vcc-quad-supply
- vcc-mixer-supply
allOf:
- $ref: /schemas/spi/spi-peripheral-props.yaml#
@@ -0,0 +1,49 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/light/rohm,bu27010.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: ROHM BU27010 color sensor
maintainers:
- Matti Vaittinen <mazziesaccount@gmail.com>
description: |
The ROHM BU27010 is a sensor with 6 photodiodes (red, green, blue, clear,
IR and flickering detection) with five configurable channels. Red, green
and flickering detection being always available and two out of the rest
three (blue, clear, IR) can be selected to be simultaneously measured.
Typical application is adjusting LCD/OLED backlight of TVs, mobile phones
and tablet PCs.
properties:
compatible:
const: rohm,bu27010
reg:
maxItems: 1
interrupts:
maxItems: 1
vdd-supply: true
required:
- compatible
- reg
- vdd-supply
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
light-sensor@38 {
compatible = "rohm,bu27010";
reg = <0x38>;
};
};
@@ -0,0 +1,60 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/proximity/murata,irsd200.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Murata IRS-D200 PIR sensor
maintainers:
- Waqar Hameed <waqar.hameed@axis.com>
description:
PIR sensor for human detection.
properties:
compatible:
const: murata,irsd200
reg:
items:
- enum:
- 0x48
- 0x49
description: |
When the AD pin is connected to GND, the slave address is 0x48.
When the AD pin is connected to VDD, the slave address is 0x49.
interrupts:
maxItems: 1
description:
Type should be IRQ_TYPE_EDGE_RISING.
vdd-supply:
description:
3.3 V supply voltage.
required:
- compatible
- reg
- interrupts
- vdd-supply
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
proximity@48 {
compatible = "murata,irsd200";
reg = <0x48>;
interrupts = <24 IRQ_TYPE_EDGE_RISING>;
vdd-supply = <&regulator_3v3>;
};
};
...
@@ -15,6 +15,9 @@ description: |
Specifications about the devices can be found at:
https://www.semtech.com/products/smart-sensing/sar-sensors/sx9310
allOf:
- $ref: /schemas/iio/iio.yaml#
properties:
compatible:
enum:
@@ -102,7 +105,7 @@ required:
- reg
- "#io-channel-cells"
additionalProperties: false
unevaluatedProperties: false
examples:
- |
@@ -13,6 +13,9 @@ maintainers:
description: |
Semtech's SX9324 proximity sensor.
allOf:
- $ref: /schemas/iio/iio.yaml#
properties:
compatible:
const: semtech,sx9324
@@ -167,7 +170,7 @@ required:
- reg
- "#io-channel-cells"
additionalProperties: false
unevaluatedProperties: false
examples:
- |
@@ -25,14 +25,20 @@ properties:
- const: qcom,msm8998-bwmon # BWMON v4
- items:
- enum:
- qcom,sc7180-cpu-bwmon
- qcom,sc7280-cpu-bwmon
- qcom,sc8280xp-cpu-bwmon
- qcom,sdm845-cpu-bwmon
- qcom,sm6350-llcc-bwmon
- qcom,sm8250-cpu-bwmon
- qcom,sm8550-cpu-bwmon
- const: qcom,sdm845-bwmon # BWMON v4, unified register space
- items:
- enum:
- qcom,sc7180-llcc-bwmon
- qcom,sc8280xp-llcc-bwmon
- qcom,sm6350-cpu-bwmon
- qcom,sm8250-llcc-bwmon
- qcom,sm8550-llcc-bwmon
- const: qcom,sc7280-llcc-bwmon
- const: qcom,sc7280-llcc-bwmon # BWMON v5
@@ -21,6 +21,7 @@ properties:
- enum:
- qcom,sc7180-osm-l3
- qcom,sc8180x-osm-l3
- qcom,sdm670-osm-l3
- qcom,sdm845-osm-l3
- qcom,sm6350-osm-l3
- qcom,sm8150-osm-l3
@@ -18,9 +18,6 @@ description: |
least one RPMh device child node pertaining to their RSC and each provider
can map to multiple RPMh resources.
allOf:
- $ref: qcom,rpmh-common.yaml#
properties:
reg:
maxItems: 1
@@ -91,6 +88,7 @@ properties:
- qcom,sm8250-mc-virt
- qcom,sm8250-mmss-noc
- qcom,sm8250-npu-noc
- qcom,sm8250-qup-virt
- qcom,sm8250-system-noc
- qcom,sm8350-aggre1-noc
- qcom,sm8350-aggre2-noc
@@ -107,7 +105,19 @@ properties:
required:
- compatible
- reg
allOf:
- $ref: qcom,rpmh-common.yaml#
- if:
not:
properties:
compatible:
enum:
- qcom,sm8250-qup-virt
then:
required:
- reg
unevaluatedProperties: false
@@ -0,0 +1,37 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/nvmem/fsl,t1023-sfp.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NXP QorIQ eFuse support
maintainers:
- Richard Alpe <richard@bit42.se>
description:
Read support for the eFuses (SFP) on NXP QorIQ series SoC's.
allOf:
- $ref: nvmem.yaml#
properties:
compatible:
const: fsl,t1023-sfp
reg:
maxItems: 1
required:
- compatible
- reg
unevaluatedProperties: false
examples:
- |
efuse@e8000 {
compatible = "fsl,t1023-sfp";
reg = <0xe8000 0x1000>;
};
...
@@ -11,6 +11,15 @@ maintainers:
- Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
properties:
compatible:
oneOf:
- const: mac-base
description: >
Cell with base MAC address to be used for calculating extra relative
addresses.
It can be stored in a plain binary format (cell length 6) or as an
ASCII text like "00:11:22:33:44:55" (cell length 17).
reg:
maxItems: 1
@@ -25,6 +34,23 @@ properties:
description:
Size in bit within the address range specified by reg.
allOf:
- if:
required: [ compatible ]
then:
if:
properties:
compatible:
contains:
const: mac-base
then:
properties:
"#nvmem-cell-cells":
description: The first argument is a MAC address offset.
const: 1
required:
- "#nvmem-cell-cells"
required:
- reg
@@ -44,6 +44,18 @@ examples:
#address-cells = <1>;
#size-cells = <1>;
mac@100 {
compatible = "mac-base";
reg = <0x100 0x6>;
#nvmem-cell-cells = <1>;
};
mac@110 {
compatible = "mac-base";
reg = <0x110 0x11>;
#nvmem-cell-cells = <1>;
};
calibration@4000 {
reg = <0x4000 0x100>;
};
@@ -49,7 +49,10 @@ properties:
patternProperties:
"@[0-9a-f]+(,[0-7])?$":
type: object
$ref: layouts/fixed-cell.yaml
allOf:
- $ref: layouts/fixed-cell.yaml
- properties:
compatible: false
deprecated: true
additionalProperties: true
@@ -23,11 +23,13 @@ properties:
- qcom,ipq8064-qfprom
- qcom,ipq8074-qfprom
- qcom,ipq9574-qfprom
- qcom,msm8226-qfprom
- qcom,msm8916-qfprom
- qcom,msm8974-qfprom
- qcom,msm8976-qfprom
- qcom,msm8996-qfprom
- qcom,msm8998-qfprom
- qcom,qcm2290-qfprom
- qcom,qcs404-qfprom
- qcom,sc7180-qfprom
- qcom,sc7280-qfprom
@@ -0,0 +1,55 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/nvmem/qcom,sec-qfprom.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm Technologies Inc, Secure QFPROM Efuse
maintainers:
- Komal Bajaj <quic_kbajaj@quicinc.com>
description:
For some of the Qualcomm SoC's, it is possible that the qfprom region is
protected from non-secure access. In such situations, the OS have to use
secure calls to read the region.
allOf:
- $ref: nvmem.yaml#
properties:
compatible:
items:
- enum:
- qcom,qdu1000-sec-qfprom
- const: qcom,sec-qfprom
reg:
items:
- description: The secure qfprom corrected region.
required:
- compatible
- reg
unevaluatedProperties: false
examples:
- |
soc {
#address-cells = <2>;
#size-cells = <2>;
efuse@221c8000 {
compatible = "qcom,qdu1000-sec-qfprom", "qcom,sec-qfprom";
reg = <0 0x221c8000 0 0x1000>;
#address-cells = <1>;
#size-cells = <1>;
multi_chan_ddr: multi-chan-ddr@12b {
reg = <0x12b 0x1>;
bits = <0 2>;
};
};
};
@@ -0,0 +1,56 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/peci/nuvoton,npcm-peci.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Nuvoton PECI Bus
maintainers:
- Tomer Maimon <tmaimon77@gmail.com>
allOf:
- $ref: peci-controller.yaml#
properties:
compatible:
enum:
- nuvoton,npcm750-peci
- nuvoton,npcm845-peci
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
description:
Clock source for PECI controller. Should reference the APB clock.
maxItems: 1
cmd-timeout-ms:
minimum: 1
maximum: 1000
default: 1000
required:
- compatible
- reg
- interrupts
- clocks
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/nuvoton,npcm7xx-clock.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
peci-controller@f0100000 {
compatible = "nuvoton,npcm750-peci";
reg = <0xf0100000 0x200>;
interrupts = <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clk NPCM7XX_CLK_APB3>;
cmd-timeout-ms = <1000>;
};
...
+25
View File
@@ -113,3 +113,28 @@ through dot to generate diagrams in many graphical formats::
$ cat /sys/kernel/debug/interconnect/interconnect_graph | \
dot -Tsvg > interconnect_graph.svg
The ``test-client`` directory provides interfaces for issuing BW requests to
any arbitrary path. Note that for safety reasons, this feature is disabled by
default without a Kconfig to enable it. Enabling it requires code changes to
``#define INTERCONNECT_ALLOW_WRITE_DEBUGFS``. Example usage::
cd /sys/kernel/debug/interconnect/test-client/
# Configure node endpoints for the path from CPU to DDR on
# qcom/sm8550.
echo chm_apps > src_node
echo ebi > dst_node
# Get path between src_node and dst_node. This is only
# necessary after updating the node endpoints.
echo 1 > get
# Set desired BW to 1GBps avg and 2GBps peak.
echo 1000000 > avg_bw
echo 2000000 > peak_bw
# Vote for avg_bw and peak_bw on the latest path from "get".
# Voting for multiple paths is possible by repeating this
# process for different nodes endpoints.
echo 1 > commit
+114 -83
View File
@@ -6,30 +6,29 @@ Everything you ever wanted to know about Linux -stable releases
Rules on what kind of patches are accepted, and which ones are not, into the
"-stable" tree:
- It or an equivalent fix must already exist in Linus' tree (upstream).
- It must be obviously correct and tested.
- It cannot be bigger than 100 lines, with context.
- It must fix only one thing.
- It must fix a real bug that bothers people (not a, "This could be a
problem..." type thing).
- It must fix a problem that causes a build error (but not for things
marked CONFIG_BROKEN), an oops, a hang, data corruption, a real
security issue, or some "oh, that's not good" issue. In short, something
critical.
- Serious issues as reported by a user of a distribution kernel may also
be considered if they fix a notable performance or interactivity issue.
As these fixes are not as obvious and have a higher risk of a subtle
regression they should only be submitted by a distribution kernel
maintainer and include an addendum linking to a bugzilla entry if it
exists and additional information on the user-visible impact.
- New device IDs and quirks are also accepted.
- No "theoretical race condition" issues, unless an explanation of how the
race can be exploited is also provided.
- It cannot contain any "trivial" fixes in it (spelling changes,
whitespace cleanups, etc).
- It must follow the
:ref:`Documentation/process/submitting-patches.rst <submittingpatches>`
rules.
- It or an equivalent fix must already exist in Linus' tree (upstream).
- It must either fix a real bug that bothers people or just add a device ID.
To elaborate on the former:
- It fixes a problem like an oops, a hang, data corruption, a real security
issue, a hardware quirk, a build error (but not for things marked
CONFIG_BROKEN), or some "oh, that's not good" issue.
- Serious issues as reported by a user of a distribution kernel may also
be considered if they fix a notable performance or interactivity issue.
As these fixes are not as obvious and have a higher risk of a subtle
regression they should only be submitted by a distribution kernel
maintainer and include an addendum linking to a bugzilla entry if it
exists and additional information on the user-visible impact.
- No "This could be a problem..." type of things like a "theoretical race
condition", unless an explanation of how the bug can be exploited is also
provided.
- No "trivial" fixes without benefit for users (spelling changes, whitespace
cleanups, etc).
Procedure for submitting patches to the -stable tree
@@ -41,32 +40,106 @@ Procedure for submitting patches to the -stable tree
process but should follow the procedures in
:ref:`Documentation/process/security-bugs.rst <securitybugs>`.
For all other submissions, choose one of the following procedures
-----------------------------------------------------------------
There are three options to submit a change to -stable trees:
1. Add a 'stable tag' to the description of a patch you then submit for
mainline inclusion.
2. Ask the stable team to pick up a patch already mainlined.
3. Submit a patch to the stable team that is equivalent to a change already
mainlined.
The sections below describe each of the options in more detail.
:ref:`option_1` is **strongly** preferred, it is the easiest and most common.
:ref:`option_2` is mainly meant for changes where backporting was not considered
at the time of submission. :ref:`option_3` is an alternative to the two earlier
options for cases where a mainlined patch needs adjustments to apply in older
series (for example due to API changes).
When using option 2 or 3 you can ask for your change to be included in specific
stable series. When doing so, ensure the fix or an equivalent is applicable,
submitted, or already present in all newer stable trees still supported. This is
meant to prevent regressions that users might later encounter on updating, if
e.g. a fix merged for 5.19-rc1 would be backported to 5.10.y, but not to 5.15.y.
.. _option_1:
Option 1
********
To have the patch automatically included in the stable tree, add the tag
To have a patch you submit for mainline inclusion later automatically picked up
for stable trees, add the tag
.. code-block:: none
Cc: stable@vger.kernel.org
in the sign-off area. Once the patch is merged it will be applied to
the stable tree without anything else needing to be done by the author
or subsystem maintainer.
in the sign-off area. Once the patch is mainlined it will be applied to the
stable tree without anything else needing to be done by the author or
subsystem maintainer.
To sent additional instructions to the stable team, use a shell-style inline
comment:
* To specify any additional patch prerequisites for cherry picking use the
following format in the sign-off area:
.. code-block:: none
Cc: <stable@vger.kernel.org> # 3.3.x: a1f84a3: sched: Check for idle
Cc: <stable@vger.kernel.org> # 3.3.x: 1b9508f: sched: Rate-limit newidle
Cc: <stable@vger.kernel.org> # 3.3.x: fd21073: sched: Fix affinity logic
Cc: <stable@vger.kernel.org> # 3.3.x
Signed-off-by: Ingo Molnar <mingo@elte.hu>
The tag sequence has the meaning of:
.. code-block:: none
git cherry-pick a1f84a3
git cherry-pick 1b9508f
git cherry-pick fd21073
git cherry-pick <this commit>
* For patches that may have kernel version prerequisites specify them using
the following format in the sign-off area:
.. code-block:: none
Cc: <stable@vger.kernel.org> # 3.3.x
The tag has the meaning of:
.. code-block:: none
git cherry-pick <this commit>
For each "-stable" tree starting with the specified version.
Note, such tagging is unnecessary if the stable team can derive the
appropriate versions from Fixes: tags.
* To delay pick up of patches, use the following format:
.. code-block:: none
Cc: <stable@vger.kernel.org> # after 4 weeks in mainline
* For any other requests, just add a note to the stable tag. This for example
can be used to point out known problems:
.. code-block:: none
Cc: <stable@vger.kernel.org> # see patch description, needs adjustments for <= 6.3
.. _option_2:
Option 2
********
After the patch has been merged to Linus' tree, send an email to
If the patch already has been merged to mainline, send an email to
stable@vger.kernel.org containing the subject of the patch, the commit ID,
why you think it should be applied, and what kernel version you wish it to
why you think it should be applied, and what kernel versions you wish it to
be applied to.
.. _option_3:
@@ -75,23 +148,9 @@ Option 3
********
Send the patch, after verifying that it follows the above rules, to
stable@vger.kernel.org. You must note the upstream commit ID in the
changelog of your submission, as well as the kernel version you wish
it to be applied to.
:ref:`option_1` is **strongly** preferred, is the easiest and most common.
:ref:`option_2` and :ref:`option_3` are more useful if the patch isn't deemed
worthy at the time it is applied to a public git tree (for instance, because
it deserves more regression testing first). :ref:`option_3` is especially
useful if the original upstream patch needs to be backported (for example
the backport needs some special handling due to e.g. API changes).
Note that for :ref:`option_3`, if the patch deviates from the original
upstream patch (for example because it had to be backported) this must be very
clearly documented and justified in the patch description.
The upstream commit ID must be specified with a separate line above the commit
text, like this:
stable@vger.kernel.org and mention the kernel versions you wish it to be applied
to. When doing so, you must note the upstream commit ID in the changelog of your
submission with a separate line above the commit text, like this:
.. code-block:: none
@@ -103,49 +162,20 @@ or alternatively:
[ Upstream commit <sha1> ]
Additionally, some patches submitted via :ref:`option_1` may have additional
patch prerequisites which can be cherry-picked. This can be specified in the
following format in the sign-off area:
If the submitted patch deviates from the original upstream patch (for example
because it had to be adjusted for the older API), this must be very clearly
documented and justified in the patch description.
.. code-block:: none
Cc: <stable@vger.kernel.org> # 3.3.x: a1f84a3: sched: Check for idle
Cc: <stable@vger.kernel.org> # 3.3.x: 1b9508f: sched: Rate-limit newidle
Cc: <stable@vger.kernel.org> # 3.3.x: fd21073: sched: Fix affinity logic
Cc: <stable@vger.kernel.org> # 3.3.x
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Following the submission
------------------------
The tag sequence has the meaning of:
The sender will receive an ACK when the patch has been accepted into the
queue, or a NAK if the patch is rejected. This response might take a few
days, according to the schedules of the stable team members.
.. code-block:: none
git cherry-pick a1f84a3
git cherry-pick 1b9508f
git cherry-pick fd21073
git cherry-pick <this commit>
Also, some patches may have kernel version prerequisites. This can be
specified in the following format in the sign-off area:
.. code-block:: none
Cc: <stable@vger.kernel.org> # 3.3.x
The tag has the meaning of:
.. code-block:: none
git cherry-pick <this commit>
For each "-stable" tree starting with the specified version.
Following the submission:
- The sender will receive an ACK when the patch has been accepted into the
queue, or a NAK if the patch is rejected. This response might take a few
days, according to the developer's schedules.
- If accepted, the patch will be added to the -stable queue, for review by
other developers and by the relevant subsystem maintainer.
If accepted, the patch will be added to the -stable queue, for review by other
developers and by the relevant subsystem maintainer.
Review cycle
@@ -174,6 +204,7 @@ Review cycle
security kernel team, and not go through the normal review cycle.
Contact the kernel security team for more details on this procedure.
Trees
-----
+2
View File
@@ -14014,12 +14014,14 @@ F: drivers/nvmem/microchip-otpc.c
F: include/dt-bindings/nvmem/microchip,sama7g5-otpc.h
MICROCHIP PCI1XXXX GP DRIVER
M: Vaibhaav Ram T.L <vaibhaavram.tl@microchip.com>
M: Kumaravel Thiagarajan <kumaravel.thiagarajan@microchip.com>
L: linux-gpio@vger.kernel.org
S: Supported
F: drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.c
F: drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gp.h
F: drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_gpio.c
F: drivers/misc/mchp_pci1xxxx/mchp_pci1xxxx_otpe2p.c
MICROCHIP PCI1XXXX I2C DRIVER
M: Tharun Kumar P <tharunkumar.pasumarthi@microchip.com>
@@ -220,6 +220,15 @@
};
};
peci: peci-controller@f0100000 {
compatible = "nuvoton,npcm750-peci";
reg = <0xf0100000 0x200>;
interrupts = <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clk NPCM7XX_CLK_APB3>;
cmd-timeout-ms = <1000>;
status = "disabled";
};
spi0: spi@200000 {
compatible = "nuvoton,npcm750-pspi";
reg = <0x200000 0x1000>;
@@ -68,6 +68,15 @@
ranges = <0x0 0x0 0xf0000000 0x00300000>,
<0xfff00000 0x0 0xfff00000 0x00016000>;
peci: peci-controller@100000 {
compatible = "nuvoton,npcm845-peci";
reg = <0x100000 0x1000>;
interrupts = <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clk NPCM8XX_CLK_APB3>;
cmd-timeout-ms = <1000>;
status = "disabled";
};
timer0: timer@8000 {
compatible = "nuvoton,npcm845-timer";
interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
-6
View File
@@ -238,12 +238,6 @@
extern int (*console_blank_hook)(int);
#endif
/*
* The apm_bios device is one of the misc char devices.
* This is its minor number.
*/
#define APM_MINOR_DEV 134
/*
* Various options can be changed at boot time as follows:
* (We allow underscores for compatibility with the modules code)
+21 -20
View File
@@ -45,7 +45,21 @@ static u64 prefetch_disable_bits;
*/
static unsigned int pseudo_lock_major;
static unsigned long pseudo_lock_minor_avail = GENMASK(MINORBITS, 0);
static struct class *pseudo_lock_class;
static char *pseudo_lock_devnode(const struct device *dev, umode_t *mode)
{
const struct rdtgroup *rdtgrp;
rdtgrp = dev_get_drvdata(dev);
if (mode)
*mode = 0600;
return kasprintf(GFP_KERNEL, "pseudo_lock/%s", rdtgrp->kn->name);
}
static const struct class pseudo_lock_class = {
.name = "pseudo_lock",
.devnode = pseudo_lock_devnode,
};
/**
* get_prefetch_disable_bits - prefetch disable bits of supported platforms
@@ -1353,7 +1367,7 @@ int rdtgroup_pseudo_lock_create(struct rdtgroup *rdtgrp)
&pseudo_measure_fops);
}
dev = device_create(pseudo_lock_class, NULL,
dev = device_create(&pseudo_lock_class, NULL,
MKDEV(pseudo_lock_major, new_minor),
rdtgrp, "%s", rdtgrp->kn->name);
@@ -1383,7 +1397,7 @@ int rdtgroup_pseudo_lock_create(struct rdtgroup *rdtgrp)
goto out;
out_device:
device_destroy(pseudo_lock_class, MKDEV(pseudo_lock_major, new_minor));
device_destroy(&pseudo_lock_class, MKDEV(pseudo_lock_major, new_minor));
out_debugfs:
debugfs_remove_recursive(plr->debugfs_dir);
pseudo_lock_minor_release(new_minor);
@@ -1424,7 +1438,7 @@ void rdtgroup_pseudo_lock_remove(struct rdtgroup *rdtgrp)
pseudo_lock_cstates_relax(plr);
debugfs_remove_recursive(rdtgrp->plr->debugfs_dir);
device_destroy(pseudo_lock_class, MKDEV(pseudo_lock_major, plr->minor));
device_destroy(&pseudo_lock_class, MKDEV(pseudo_lock_major, plr->minor));
pseudo_lock_minor_release(plr->minor);
free:
@@ -1560,16 +1574,6 @@ static const struct file_operations pseudo_lock_dev_fops = {
.mmap = pseudo_lock_dev_mmap,
};
static char *pseudo_lock_devnode(const struct device *dev, umode_t *mode)
{
const struct rdtgroup *rdtgrp;
rdtgrp = dev_get_drvdata(dev);
if (mode)
*mode = 0600;
return kasprintf(GFP_KERNEL, "pseudo_lock/%s", rdtgrp->kn->name);
}
int rdt_pseudo_lock_init(void)
{
int ret;
@@ -1580,21 +1584,18 @@ int rdt_pseudo_lock_init(void)
pseudo_lock_major = ret;
pseudo_lock_class = class_create("pseudo_lock");
if (IS_ERR(pseudo_lock_class)) {
ret = PTR_ERR(pseudo_lock_class);
ret = class_register(&pseudo_lock_class);
if (ret) {
unregister_chrdev(pseudo_lock_major, "pseudo_lock");
return ret;
}
pseudo_lock_class->devnode = pseudo_lock_devnode;
return 0;
}
void rdt_pseudo_lock_release(void)
{
class_destroy(pseudo_lock_class);
pseudo_lock_class = NULL;
class_unregister(&pseudo_lock_class);
unregister_chrdev(pseudo_lock_major, "pseudo_lock");
pseudo_lock_major = 0;
}
+16 -15
View File
@@ -40,7 +40,6 @@
#include <asm/processor.h>
#include <asm/msr.h>
static struct class *cpuid_class;
static enum cpuhp_state cpuhp_cpuid_state;
struct cpuid_regs_done {
@@ -124,26 +123,31 @@ static const struct file_operations cpuid_fops = {
.open = cpuid_open,
};
static char *cpuid_devnode(const struct device *dev, umode_t *mode)
{
return kasprintf(GFP_KERNEL, "cpu/%u/cpuid", MINOR(dev->devt));
}
static const struct class cpuid_class = {
.name = "cpuid",
.devnode = cpuid_devnode,
};
static int cpuid_device_create(unsigned int cpu)
{
struct device *dev;
dev = device_create(cpuid_class, NULL, MKDEV(CPUID_MAJOR, cpu), NULL,
dev = device_create(&cpuid_class, NULL, MKDEV(CPUID_MAJOR, cpu), NULL,
"cpu%d", cpu);
return PTR_ERR_OR_ZERO(dev);
}
static int cpuid_device_destroy(unsigned int cpu)
{
device_destroy(cpuid_class, MKDEV(CPUID_MAJOR, cpu));
device_destroy(&cpuid_class, MKDEV(CPUID_MAJOR, cpu));
return 0;
}
static char *cpuid_devnode(const struct device *dev, umode_t *mode)
{
return kasprintf(GFP_KERNEL, "cpu/%u/cpuid", MINOR(dev->devt));
}
static int __init cpuid_init(void)
{
int err;
@@ -154,12 +158,9 @@ static int __init cpuid_init(void)
CPUID_MAJOR);
return -EBUSY;
}
cpuid_class = class_create("cpuid");
if (IS_ERR(cpuid_class)) {
err = PTR_ERR(cpuid_class);
err = class_register(&cpuid_class);
if (err)
goto out_chrdev;
}
cpuid_class->devnode = cpuid_devnode;
err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "x86/cpuid:online",
cpuid_device_create, cpuid_device_destroy);
@@ -170,7 +171,7 @@ static int __init cpuid_init(void)
return 0;
out_class:
class_destroy(cpuid_class);
class_unregister(&cpuid_class);
out_chrdev:
__unregister_chrdev(CPUID_MAJOR, 0, NR_CPUS, "cpu/cpuid");
return err;
@@ -180,7 +181,7 @@ module_init(cpuid_init);
static void __exit cpuid_exit(void)
{
cpuhp_remove_state(cpuhp_cpuid_state);
class_destroy(cpuid_class);
class_unregister(&cpuid_class);
__unregister_chrdev(CPUID_MAJOR, 0, NR_CPUS, "cpu/cpuid");
}
module_exit(cpuid_exit);
+16 -15
View File
@@ -39,7 +39,6 @@
#include <asm/cpufeature.h>
#include <asm/msr.h>
static struct class *msr_class;
static enum cpuhp_state cpuhp_msr_state;
enum allow_write_msrs {
@@ -235,26 +234,31 @@ static const struct file_operations msr_fops = {
.compat_ioctl = msr_ioctl,
};
static char *msr_devnode(const struct device *dev, umode_t *mode)
{
return kasprintf(GFP_KERNEL, "cpu/%u/msr", MINOR(dev->devt));
}
static const struct class msr_class = {
.name = "msr",
.devnode = msr_devnode,
};
static int msr_device_create(unsigned int cpu)
{
struct device *dev;
dev = device_create(msr_class, NULL, MKDEV(MSR_MAJOR, cpu), NULL,
dev = device_create(&msr_class, NULL, MKDEV(MSR_MAJOR, cpu), NULL,
"msr%d", cpu);
return PTR_ERR_OR_ZERO(dev);
}
static int msr_device_destroy(unsigned int cpu)
{
device_destroy(msr_class, MKDEV(MSR_MAJOR, cpu));
device_destroy(&msr_class, MKDEV(MSR_MAJOR, cpu));
return 0;
}
static char *msr_devnode(const struct device *dev, umode_t *mode)
{
return kasprintf(GFP_KERNEL, "cpu/%u/msr", MINOR(dev->devt));
}
static int __init msr_init(void)
{
int err;
@@ -263,12 +267,9 @@ static int __init msr_init(void)
pr_err("unable to get major %d for msr\n", MSR_MAJOR);
return -EBUSY;
}
msr_class = class_create("msr");
if (IS_ERR(msr_class)) {
err = PTR_ERR(msr_class);
err = class_register(&msr_class);
if (err)
goto out_chrdev;
}
msr_class->devnode = msr_devnode;
err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "x86/msr:online",
msr_device_create, msr_device_destroy);
@@ -278,7 +279,7 @@ static int __init msr_init(void)
return 0;
out_class:
class_destroy(msr_class);
class_unregister(&msr_class);
out_chrdev:
__unregister_chrdev(MSR_MAJOR, 0, NR_CPUS, "cpu/msr");
return err;
@@ -288,7 +289,7 @@ module_init(msr_init);
static void __exit msr_exit(void)
{
cpuhp_remove_state(cpuhp_msr_state);
class_destroy(msr_class);
class_unregister(&msr_class);
__unregister_chrdev(MSR_MAJOR, 0, NR_CPUS, "cpu/msr");
}
module_exit(msr_exit)
-1
View File
@@ -22,7 +22,6 @@
static const struct acpi_device_id amba_id_list[] = {
{"ARMH0061", 0}, /* PL061 GPIO Device */
{"ARMH0330", 0}, /* ARM DMA Controller DMA-330 */
{"ARMHC500", 0}, /* ARM CoreSight ETM4x */
{"ARMHC501", 0}, /* ARM CoreSight ETR */
{"ARMHC502", 0}, /* ARM CoreSight STM */
{"ARMHC503", 0}, /* ARM CoreSight Debug */
+1
View File
@@ -528,6 +528,7 @@ static void amba_device_release(struct device *dev)
{
struct amba_device *d = to_amba_device(dev);
of_node_put(d->dev.of_node);
if (d->res.parent)
release_resource(&d->res);
mutex_destroy(&d->periphid_lock);
+9 -11
View File
@@ -6758,6 +6758,7 @@ static int __init binder_init(void)
struct binder_device *device;
struct hlist_node *tmp;
char *device_names = NULL;
const struct binder_debugfs_entry *db_entry;
ret = binder_alloc_shrinker_init();
if (ret)
@@ -6767,19 +6768,16 @@ static int __init binder_init(void)
atomic_set(&binder_transaction_log_failed.cur, ~0U);
binder_debugfs_dir_entry_root = debugfs_create_dir("binder", NULL);
if (binder_debugfs_dir_entry_root) {
const struct binder_debugfs_entry *db_entry;
binder_for_each_debugfs_entry(db_entry)
debugfs_create_file(db_entry->name,
db_entry->mode,
binder_debugfs_dir_entry_root,
db_entry->data,
db_entry->fops);
binder_for_each_debugfs_entry(db_entry)
debugfs_create_file(db_entry->name,
db_entry->mode,
binder_debugfs_dir_entry_root,
db_entry->data,
db_entry->fops);
binder_debugfs_dir_entry_proc = debugfs_create_dir("proc",
binder_debugfs_dir_entry_root);
}
binder_debugfs_dir_entry_proc = debugfs_create_dir("proc",
binder_debugfs_dir_entry_root);
if (!IS_ENABLED(CONFIG_ANDROID_BINDERFS) &&
strcmp(binder_devices_param, "") != 0) {
-1
View File
@@ -19,7 +19,6 @@
#include <linux/mutex.h>
#include <linux/mount.h>
#include <linux/fs_parser.h>
#include <linux/radix-tree.h>
#include <linux/sched.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
+20 -10
View File
@@ -2306,12 +2306,12 @@ static void device_platform_notify(struct device *dev)
static void device_platform_notify_remove(struct device *dev)
{
acpi_device_notify_remove(dev);
if (platform_notify_remove)
platform_notify_remove(dev);
software_node_notify_remove(dev);
if (platform_notify_remove)
platform_notify_remove(dev);
acpi_device_notify_remove(dev);
}
/**
@@ -3528,18 +3528,17 @@ int device_add(struct device *dev)
* the name, and force the use of dev_name()
*/
if (dev->init_name) {
dev_set_name(dev, "%s", dev->init_name);
error = dev_set_name(dev, "%s", dev->init_name);
dev->init_name = NULL;
}
if (dev_name(dev))
error = 0;
/* subsystems can specify simple device enumeration */
if (!dev_name(dev) && dev->bus && dev->bus->dev_name)
dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);
if (!dev_name(dev)) {
error = -EINVAL;
else if (dev->bus && dev->bus->dev_name)
error = dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);
if (error)
goto name_error;
}
pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
@@ -3815,6 +3814,17 @@ void device_del(struct device *dev)
device_platform_notify_remove(dev);
device_links_purge(dev);
/*
* If a device does not have a driver attached, we need to clean
* up any managed resources. We do this in device_release(), but
* it's never called (and we leak the device) if a managed
* resource holds a reference to the device. So release all
* managed resources here, like we do in driver_detach(). We
* still need to do so again in device_release() in case someone
* adds a new resource after this point, though.
*/
devres_release_all(dev);
bus_notify(dev, BUS_NOTIFY_REMOVED_DEVICE);
kobject_uevent(&dev->kobj, KOBJ_REMOVE);
glue_dir = get_glue_dir(dev);
+19
View File
@@ -19,6 +19,7 @@
#include <linux/cpufeature.h>
#include <linux/tick.h>
#include <linux/pm_qos.h>
#include <linux/delay.h>
#include <linux/sched/isolation.h>
#include "base.h"
@@ -50,12 +51,30 @@ static int cpu_subsys_online(struct device *dev)
int cpuid = dev->id;
int from_nid, to_nid;
int ret;
int retries = 0;
from_nid = cpu_to_node(cpuid);
if (from_nid == NUMA_NO_NODE)
return -ENODEV;
retry:
ret = cpu_device_up(dev);
/*
* If -EBUSY is returned, it is likely that hotplug is temporarily
* disabled when cpu_hotplug_disable() was called. This condition is
* transient. So we retry after waiting for an exponentially
* increasing delay up to a total of at least 620ms as some PCI
* device initialization can take quite a while.
*/
if (ret == -EBUSY) {
retries++;
if (retries > 5)
return ret;
msleep(10 * (1 << retries));
goto retry;
}
/*
* When hot adding memory to memoryless node and enabling a cpu
* on the node, node number of the cpu may internally change.
+2
View File
@@ -693,6 +693,8 @@ re_probe:
device_remove(dev);
driver_sysfs_remove(dev);
if (dev->bus && dev->bus->dma_cleanup)
dev->bus->dma_cleanup(dev);
device_unbind_cleanup(dev);
goto re_probe;
-1
View File
@@ -20,7 +20,6 @@
#include <linux/pm_runtime.h>
#include <linux/swap.h>
#include <linux/slab.h>
#include <linux/hugetlb.h>
static struct bus_type node_subsys = {
.name = "node",
+2
View File
@@ -0,0 +1,2 @@
CONFIG_KUNIT=y
CONFIG_DM_KUNIT_TEST=y
+4
View File
@@ -9,6 +9,10 @@ config TEST_ASYNC_DRIVER_PROBE
If unsure say N.
config DM_KUNIT_TEST
tristate "KUnit Tests for the device model" if !KUNIT_ALL_TESTS
depends on KUNIT
config DRIVER_PE_KUNIT_TEST
bool "KUnit Tests for property entry API" if !KUNIT_ALL_TESTS
depends on KUNIT=y
+3
View File
@@ -1,5 +1,8 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_TEST_ASYNC_DRIVER_PROBE) += test_async_driver_probe.o
obj-$(CONFIG_DM_KUNIT_TEST) += root-device-test.o
obj-$(CONFIG_DM_KUNIT_TEST) += platform-device-test.o
obj-$(CONFIG_DRIVER_PE_KUNIT_TEST) += property-entry-test.o
CFLAGS_property-entry-test.o += $(DISABLE_STRUCTLEAK_PLUGIN)
+224
View File
@@ -0,0 +1,224 @@
// SPDX-License-Identifier: GPL-2.0
#include <kunit/resource.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#define DEVICE_NAME "test"
struct test_priv {
bool probe_done;
bool release_done;
wait_queue_head_t probe_wq;
wait_queue_head_t release_wq;
struct device *dev;
};
static int platform_device_devm_init(struct kunit *test)
{
struct test_priv *priv;
priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
init_waitqueue_head(&priv->probe_wq);
init_waitqueue_head(&priv->release_wq);
test->priv = priv;
return 0;
}
static void devm_device_action(void *ptr)
{
struct test_priv *priv = ptr;
priv->release_done = true;
wake_up_interruptible(&priv->release_wq);
}
static void devm_put_device_action(void *ptr)
{
struct test_priv *priv = ptr;
put_device(priv->dev);
priv->release_done = true;
wake_up_interruptible(&priv->release_wq);
}
#define RELEASE_TIMEOUT_MS 100
/*
* Tests that a platform bus, non-probed device will run its
* device-managed actions when unregistered.
*/
static void platform_device_devm_register_unregister_test(struct kunit *test)
{
struct platform_device *pdev;
struct test_priv *priv = test->priv;
int ret;
pdev = platform_device_alloc(DEVICE_NAME, PLATFORM_DEVID_NONE);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
ret = platform_device_add(pdev);
KUNIT_ASSERT_EQ(test, ret, 0);
priv->dev = &pdev->dev;
ret = devm_add_action_or_reset(priv->dev, devm_device_action, priv);
KUNIT_ASSERT_EQ(test, ret, 0);
platform_device_unregister(pdev);
ret = wait_event_interruptible_timeout(priv->release_wq, priv->release_done,
msecs_to_jiffies(RELEASE_TIMEOUT_MS));
KUNIT_EXPECT_GT(test, ret, 0);
}
/*
* Tests that a platform bus, non-probed device will run its
* device-managed actions when unregistered, even if someone still holds
* a reference to it.
*/
static void platform_device_devm_register_get_unregister_with_devm_test(struct kunit *test)
{
struct platform_device *pdev;
struct test_priv *priv = test->priv;
int ret;
pdev = platform_device_alloc(DEVICE_NAME, PLATFORM_DEVID_NONE);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
ret = platform_device_add(pdev);
KUNIT_ASSERT_EQ(test, ret, 0);
priv->dev = &pdev->dev;
get_device(priv->dev);
ret = devm_add_action_or_reset(priv->dev, devm_put_device_action, priv);
KUNIT_ASSERT_EQ(test, ret, 0);
platform_device_unregister(pdev);
ret = wait_event_interruptible_timeout(priv->release_wq, priv->release_done,
msecs_to_jiffies(RELEASE_TIMEOUT_MS));
KUNIT_EXPECT_GT(test, ret, 0);
}
static int fake_probe(struct platform_device *pdev)
{
struct test_priv *priv = platform_get_drvdata(pdev);
priv->probe_done = true;
wake_up_interruptible(&priv->probe_wq);
return 0;
}
static struct platform_driver fake_driver = {
.probe = fake_probe,
.driver = {
.name = DEVICE_NAME,
},
};
/*
* Tests that a platform bus, probed device will run its device-managed
* actions when unregistered.
*/
static void probed_platform_device_devm_register_unregister_test(struct kunit *test)
{
struct platform_device *pdev;
struct test_priv *priv = test->priv;
int ret;
ret = platform_driver_register(&fake_driver);
KUNIT_ASSERT_EQ(test, ret, 0);
pdev = platform_device_alloc(DEVICE_NAME, PLATFORM_DEVID_NONE);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
priv->dev = &pdev->dev;
platform_set_drvdata(pdev, priv);
ret = platform_device_add(pdev);
KUNIT_ASSERT_EQ(test, ret, 0);
ret = wait_event_interruptible_timeout(priv->probe_wq, priv->probe_done,
msecs_to_jiffies(RELEASE_TIMEOUT_MS));
KUNIT_ASSERT_GT(test, ret, 0);
ret = devm_add_action_or_reset(priv->dev, devm_device_action, priv);
KUNIT_ASSERT_EQ(test, ret, 0);
platform_device_unregister(pdev);
ret = wait_event_interruptible_timeout(priv->release_wq, priv->release_done,
msecs_to_jiffies(RELEASE_TIMEOUT_MS));
KUNIT_EXPECT_GT(test, ret, 0);
platform_driver_unregister(&fake_driver);
}
/*
* Tests that a platform bus, probed device will run its device-managed
* actions when unregistered, even if someone still holds a reference to
* it.
*/
static void probed_platform_device_devm_register_get_unregister_with_devm_test(struct kunit *test)
{
struct platform_device *pdev;
struct test_priv *priv = test->priv;
int ret;
ret = platform_driver_register(&fake_driver);
KUNIT_ASSERT_EQ(test, ret, 0);
pdev = platform_device_alloc(DEVICE_NAME, PLATFORM_DEVID_NONE);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
priv->dev = &pdev->dev;
platform_set_drvdata(pdev, priv);
ret = platform_device_add(pdev);
KUNIT_ASSERT_EQ(test, ret, 0);
ret = wait_event_interruptible_timeout(priv->probe_wq, priv->probe_done,
msecs_to_jiffies(RELEASE_TIMEOUT_MS));
KUNIT_ASSERT_GT(test, ret, 0);
get_device(priv->dev);
ret = devm_add_action_or_reset(priv->dev, devm_put_device_action, priv);
KUNIT_ASSERT_EQ(test, ret, 0);
platform_device_unregister(pdev);
ret = wait_event_interruptible_timeout(priv->release_wq, priv->release_done,
msecs_to_jiffies(RELEASE_TIMEOUT_MS));
KUNIT_EXPECT_GT(test, ret, 0);
platform_driver_unregister(&fake_driver);
}
static struct kunit_case platform_device_devm_tests[] = {
KUNIT_CASE(platform_device_devm_register_unregister_test),
KUNIT_CASE(platform_device_devm_register_get_unregister_with_devm_test),
KUNIT_CASE(probed_platform_device_devm_register_unregister_test),
KUNIT_CASE(probed_platform_device_devm_register_get_unregister_with_devm_test),
{}
};
static struct kunit_suite platform_device_devm_test_suite = {
.name = "platform-device-devm",
.init = platform_device_devm_init,
.test_cases = platform_device_devm_tests,
};
kunit_test_suite(platform_device_devm_test_suite);
MODULE_DESCRIPTION("Test module for platform devices");
MODULE_AUTHOR("Maxime Ripard <mripard@kernel.org>");
MODULE_LICENSE("GPL");
+112
View File
@@ -0,0 +1,112 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright 2023 Maxime Ripard <mripard@kernel.org>
#include <kunit/resource.h>
#include <linux/device.h>
#define DEVICE_NAME "test"
struct test_priv {
bool probe_done;
bool release_done;
wait_queue_head_t release_wq;
struct device *dev;
};
static int root_device_devm_init(struct kunit *test)
{
struct test_priv *priv;
priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
init_waitqueue_head(&priv->release_wq);
test->priv = priv;
return 0;
}
static void devm_device_action(void *ptr)
{
struct test_priv *priv = ptr;
priv->release_done = true;
wake_up_interruptible(&priv->release_wq);
}
#define RELEASE_TIMEOUT_MS 100
/*
* Tests that a bus-less, non-probed device will run its device-managed
* actions when unregistered.
*/
static void root_device_devm_register_unregister_test(struct kunit *test)
{
struct test_priv *priv = test->priv;
int ret;
priv->dev = root_device_register(DEVICE_NAME);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->dev);
ret = devm_add_action_or_reset(priv->dev, devm_device_action, priv);
KUNIT_ASSERT_EQ(test, ret, 0);
root_device_unregister(priv->dev);
ret = wait_event_interruptible_timeout(priv->release_wq, priv->release_done,
msecs_to_jiffies(RELEASE_TIMEOUT_MS));
KUNIT_EXPECT_GT(test, ret, 0);
}
static void devm_put_device_action(void *ptr)
{
struct test_priv *priv = ptr;
put_device(priv->dev);
priv->release_done = true;
wake_up_interruptible(&priv->release_wq);
}
/*
* Tests that a bus-less, non-probed device will run its device-managed
* actions when unregistered, even if someone still holds a reference to
* it.
*/
static void root_device_devm_register_get_unregister_with_devm_test(struct kunit *test)
{
struct test_priv *priv = test->priv;
int ret;
priv->dev = root_device_register(DEVICE_NAME);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->dev);
get_device(priv->dev);
ret = devm_add_action_or_reset(priv->dev, devm_put_device_action, priv);
KUNIT_ASSERT_EQ(test, ret, 0);
root_device_unregister(priv->dev);
ret = wait_event_interruptible_timeout(priv->release_wq, priv->release_done,
msecs_to_jiffies(RELEASE_TIMEOUT_MS));
KUNIT_EXPECT_GT(test, ret, 0);
}
static struct kunit_case root_device_devm_tests[] = {
KUNIT_CASE(root_device_devm_register_unregister_test),
KUNIT_CASE(root_device_devm_register_get_unregister_with_devm_test),
{}
};
static struct kunit_suite root_device_devm_test_suite = {
.name = "root-device-devm",
.init = root_device_devm_init,
.test_cases = root_device_devm_tests,
};
kunit_test_suite(root_device_devm_test_suite);
MODULE_DESCRIPTION("Test module for root devices");
MODULE_AUTHOR("Maxime Ripard <mripard@kernel.org>");
MODULE_LICENSE("GPL");
+1 -1
View File
@@ -84,7 +84,7 @@ test_platform_device_register_node(char *name, int id, int nid)
pdev = platform_device_alloc(name, id);
if (!pdev)
return NULL;
return ERR_PTR(-ENOMEM);
if (nid != NUMA_NO_NODE)
set_dev_node(&pdev->dev, nid);
+25 -9
View File
@@ -365,12 +365,10 @@ error_alloc_mhi_buf:
}
static void mhi_firmware_copy(struct mhi_controller *mhi_cntrl,
const struct firmware *firmware,
const u8 *buf, size_t remainder,
struct image_info *img_info)
{
size_t remainder = firmware->size;
size_t to_cpy;
const u8 *buf = firmware->data;
struct mhi_buf *mhi_buf = img_info->mhi_buf;
struct bhi_vec_entry *bhi_vec = img_info->bhi_vec;
@@ -393,9 +391,10 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl)
struct device *dev = &mhi_cntrl->mhi_dev->dev;
enum mhi_pm_state new_state;
const char *fw_name;
const u8 *fw_data;
void *buf;
dma_addr_t dma_addr;
size_t size;
size_t size, fw_sz;
int i, ret;
if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
@@ -425,6 +424,20 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl)
fw_name = (mhi_cntrl->ee == MHI_EE_EDL) ?
mhi_cntrl->edl_image : mhi_cntrl->fw_image;
/* check if the driver has already provided the firmware data */
if (!fw_name && mhi_cntrl->fbc_download &&
mhi_cntrl->fw_data && mhi_cntrl->fw_sz) {
if (!mhi_cntrl->sbl_size) {
dev_err(dev, "fw_data provided but no sbl_size\n");
goto error_fw_load;
}
size = mhi_cntrl->sbl_size;
fw_data = mhi_cntrl->fw_data;
fw_sz = mhi_cntrl->fw_sz;
goto skip_req_fw;
}
if (!fw_name || (mhi_cntrl->fbc_download && (!mhi_cntrl->sbl_size ||
!mhi_cntrl->seg_len))) {
dev_err(dev,
@@ -444,6 +457,10 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl)
if (size > firmware->size)
size = firmware->size;
fw_data = firmware->data;
fw_sz = firmware->size;
skip_req_fw:
buf = dma_alloc_coherent(mhi_cntrl->cntrl_dev, size, &dma_addr,
GFP_KERNEL);
if (!buf) {
@@ -452,7 +469,7 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl)
}
/* Download image using BHI */
memcpy(buf, firmware->data, size);
memcpy(buf, fw_data, size);
ret = mhi_fw_load_bhi(mhi_cntrl, dma_addr, size);
dma_free_coherent(mhi_cntrl->cntrl_dev, size, buf, dma_addr);
@@ -464,7 +481,7 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl)
}
/* Wait for ready since EDL image was loaded */
if (fw_name == mhi_cntrl->edl_image) {
if (fw_name && fw_name == mhi_cntrl->edl_image) {
release_firmware(firmware);
goto fw_load_ready_state;
}
@@ -478,15 +495,14 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl)
* device transitioning into MHI READY state
*/
if (mhi_cntrl->fbc_download) {
ret = mhi_alloc_bhie_table(mhi_cntrl, &mhi_cntrl->fbc_image,
firmware->size);
ret = mhi_alloc_bhie_table(mhi_cntrl, &mhi_cntrl->fbc_image, fw_sz);
if (ret) {
release_firmware(firmware);
goto error_fw_load;
}
/* Load the firmware into BHIE vec table */
mhi_firmware_copy(mhi_cntrl, firmware, mhi_cntrl->fbc_image);
mhi_firmware_copy(mhi_cntrl, fw_data, fw_sz, mhi_cntrl->fbc_image);
}
release_firmware(firmware);
+1 -1
View File
@@ -759,7 +759,7 @@ static int parse_ch_cfg(struct mhi_controller *mhi_cntrl,
* so to avoid any memory possible allocation failures, vzalloc is
* used here
*/
mhi_cntrl->mhi_chan = vzalloc(mhi_cntrl->max_chan *
mhi_cntrl->mhi_chan = vcalloc(mhi_cntrl->max_chan,
sizeof(*mhi_cntrl->mhi_chan));
if (!mhi_cntrl->mhi_chan)
return -ENOMEM;
-1
View File
@@ -938,7 +938,6 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl,
if (!mhi_chan->configured)
break;
parse_xfer_event(mhi_cntrl, local_rp, mhi_chan);
event_quota--;
}
break;
default:
+49 -4
View File
@@ -212,6 +212,19 @@ struct mhi_pci_dev_info {
.offload_channel = false, \
}
#define MHI_EVENT_CONFIG_SW_DATA(ev_ring, el_count) \
{ \
.num_elements = el_count, \
.irq_moderation_ms = 0, \
.irq = (ev_ring) + 1, \
.priority = 1, \
.mode = MHI_DB_BRST_DISABLE, \
.data_type = MHI_ER_DATA, \
.hardware_event = false, \
.client_managed = false, \
.offload_channel = false, \
}
#define MHI_EVENT_CONFIG_HW_DATA(ev_ring, el_count, ch_num) \
{ \
.num_elements = el_count, \
@@ -237,8 +250,10 @@ static const struct mhi_channel_config modem_qcom_v1_mhi_channels[] = {
MHI_CHANNEL_CONFIG_DL_AUTOQUEUE(21, "IPCR", 8, 0),
MHI_CHANNEL_CONFIG_UL_FP(34, "FIREHOSE", 32, 0),
MHI_CHANNEL_CONFIG_DL_FP(35, "FIREHOSE", 32, 0),
MHI_CHANNEL_CONFIG_HW_UL(100, "IP_HW0", 128, 2),
MHI_CHANNEL_CONFIG_HW_DL(101, "IP_HW0", 128, 3),
MHI_CHANNEL_CONFIG_UL(46, "IP_SW0", 64, 2),
MHI_CHANNEL_CONFIG_DL(47, "IP_SW0", 64, 3),
MHI_CHANNEL_CONFIG_HW_UL(100, "IP_HW0", 128, 4),
MHI_CHANNEL_CONFIG_HW_DL(101, "IP_HW0", 128, 5),
};
static struct mhi_event_config modem_qcom_v1_mhi_events[] = {
@@ -246,9 +261,12 @@ static struct mhi_event_config modem_qcom_v1_mhi_events[] = {
MHI_EVENT_CONFIG_CTRL(0, 64),
/* DIAG dedicated event ring */
MHI_EVENT_CONFIG_DATA(1, 128),
/* Software channels dedicated event ring */
MHI_EVENT_CONFIG_SW_DATA(2, 64),
MHI_EVENT_CONFIG_SW_DATA(3, 64),
/* Hardware channels request dedicated hardware event rings */
MHI_EVENT_CONFIG_HW_DATA(2, 1024, 100),
MHI_EVENT_CONFIG_HW_DATA(3, 2048, 101)
MHI_EVENT_CONFIG_HW_DATA(4, 1024, 100),
MHI_EVENT_CONFIG_HW_DATA(5, 2048, 101)
};
static const struct mhi_controller_config modem_qcom_v1_mhiv_config = {
@@ -334,6 +352,16 @@ static const struct mhi_pci_dev_info mhi_quectel_em1xx_info = {
.sideband_wake = true,
};
static const struct mhi_pci_dev_info mhi_quectel_rm5xx_info = {
.name = "quectel-rm5xx",
.edl = "qcom/prog_firehose_sdx6x.elf",
.config = &modem_quectel_em1xx_config,
.bar_num = MHI_PCI_DEFAULT_BAR_NUM,
.dma_data_width = 32,
.mru_default = 32768,
.sideband_wake = true,
};
static const struct mhi_channel_config mhi_foxconn_sdx55_channels[] = {
MHI_CHANNEL_CONFIG_UL(0, "LOOPBACK", 32, 0),
MHI_CHANNEL_CONFIG_DL(1, "LOOPBACK", 32, 0),
@@ -567,12 +595,23 @@ static const struct pci_device_id mhi_pci_id_table[] = {
/* Telit FN990 */
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0308, 0x1c5d, 0x2010),
.driver_data = (kernel_ulong_t) &mhi_telit_fn990_info },
/* Telit FE990 */
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0308, 0x1c5d, 0x2015),
.driver_data = (kernel_ulong_t) &mhi_telit_fn990_info },
{ PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x0308),
.driver_data = (kernel_ulong_t) &mhi_qcom_sdx65_info },
{ PCI_DEVICE(PCI_VENDOR_ID_QUECTEL, 0x1001), /* EM120R-GL (sdx24) */
.driver_data = (kernel_ulong_t) &mhi_quectel_em1xx_info },
{ PCI_DEVICE(PCI_VENDOR_ID_QUECTEL, 0x1002), /* EM160R-GL (sdx24) */
.driver_data = (kernel_ulong_t) &mhi_quectel_em1xx_info },
/* RM520N-GL (sdx6x), eSIM */
{ PCI_DEVICE(PCI_VENDOR_ID_QUECTEL, 0x1004),
.driver_data = (kernel_ulong_t) &mhi_quectel_rm5xx_info },
/* RM520N-GL (sdx6x), Lenovo variant */
{ PCI_DEVICE(PCI_VENDOR_ID_QUECTEL, 0x1007),
.driver_data = (kernel_ulong_t) &mhi_quectel_rm5xx_info },
{ PCI_DEVICE(PCI_VENDOR_ID_QUECTEL, 0x100d), /* EM160R-GL (sdx24) */
.driver_data = (kernel_ulong_t) &mhi_quectel_em1xx_info },
{ PCI_DEVICE(PCI_VENDOR_ID_QUECTEL, 0x2001), /* EM120R-GL for FCCL (sdx24) */
.driver_data = (kernel_ulong_t) &mhi_quectel_em1xx_info },
/* T99W175 (sdx55), Both for eSIM and Non-eSIM */
@@ -605,6 +644,12 @@ static const struct pci_device_id mhi_pci_id_table[] = {
/* T99W510 (sdx24), variant 3 */
{ PCI_DEVICE(PCI_VENDOR_ID_FOXCONN, 0xe0f2),
.driver_data = (kernel_ulong_t) &mhi_foxconn_sdx24_info },
/* DW5932e-eSIM (sdx62), With eSIM */
{ PCI_DEVICE(PCI_VENDOR_ID_FOXCONN, 0xe0f5),
.driver_data = (kernel_ulong_t) &mhi_foxconn_sdx65_info },
/* DW5932e (sdx62), Non-eSIM */
{ PCI_DEVICE(PCI_VENDOR_ID_FOXCONN, 0xe0f9),
.driver_data = (kernel_ulong_t) &mhi_foxconn_sdx65_info },
/* MV31-W (Cinterion) */
{ PCI_DEVICE(PCI_VENDOR_ID_THALES, 0x00b3),
.driver_data = (kernel_ulong_t) &mhi_mv31_info },
+5
View File
@@ -470,6 +470,10 @@ static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl)
/* Trigger MHI RESET so that the device will not access host memory */
if (!MHI_PM_IN_FATAL_STATE(mhi_cntrl->pm_state)) {
/* Skip MHI RESET if in RDDM state */
if (mhi_cntrl->rddm_image && mhi_get_exec_env(mhi_cntrl) == MHI_EE_RDDM)
goto skip_mhi_reset;
dev_dbg(dev, "Triggering MHI Reset in device\n");
mhi_set_mhi_state(mhi_cntrl, MHI_STATE_RESET);
@@ -495,6 +499,7 @@ static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl)
}
}
skip_mhi_reset:
dev_dbg(dev,
"Waiting for all pending event ring processing to complete\n");
mhi_event = mhi_cntrl->mhi_event;
+2 -1
View File
@@ -5,7 +5,8 @@
* Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
*/
#include <linux/of_platform.h>
#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/cdx/cdx_bus.h>
+2 -1
View File
@@ -7,7 +7,8 @@
#include <linux/rpmsg.h>
#include <linux/remoteproc.h>
#include <linux/of_platform.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/cdx/cdx_bus.h>
#include <linux/module.h>
+1
View File
@@ -3,6 +3,7 @@
* UniNorth AGPGART routines.
*/
#include <linux/module.h>
#include <linux/of.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/init.h>
+1 -2
View File
@@ -6,11 +6,10 @@
* Author: Sonny Rao <sonnyrao@us.ibm.com>
*/
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/cdev.h>
+8 -3
View File
@@ -28,8 +28,13 @@
DEFINE_IDR(dev_nums_idr);
static DEFINE_MUTEX(idr_lock);
struct class *tpm_class;
struct class *tpmrm_class;
const struct class tpm_class = {
.name = "tpm",
.shutdown_pre = tpm_class_shutdown,
};
const struct class tpmrm_class = {
.name = "tmprm",
};
dev_t tpm_devt;
static int tpm_request_locality(struct tpm_chip *chip)
@@ -336,7 +341,7 @@ struct tpm_chip *tpm_chip_alloc(struct device *pdev,
device_initialize(&chip->dev);
chip->dev.class = tpm_class;
chip->dev.class = &tpm_class;
chip->dev.release = tpm_dev_release;
chip->dev.parent = pdev;
chip->dev.groups = chip->groups;
+9 -12
View File
@@ -476,18 +476,15 @@ static int __init tpm_init(void)
{
int rc;
tpm_class = class_create("tpm");
if (IS_ERR(tpm_class)) {
rc = class_register(&tpm_class);
if (rc) {
pr_err("couldn't create tpm class\n");
return PTR_ERR(tpm_class);
return rc;
}
tpm_class->shutdown_pre = tpm_class_shutdown;
tpmrm_class = class_create("tpmrm");
if (IS_ERR(tpmrm_class)) {
rc = class_register(&tpmrm_class);
if (rc) {
pr_err("couldn't create tpmrm class\n");
rc = PTR_ERR(tpmrm_class);
goto out_destroy_tpm_class;
}
@@ -508,9 +505,9 @@ static int __init tpm_init(void)
out_unreg_chrdev:
unregister_chrdev_region(tpm_devt, 2 * TPM_NUM_DEVICES);
out_destroy_tpmrm_class:
class_destroy(tpmrm_class);
class_unregister(&tpmrm_class);
out_destroy_tpm_class:
class_destroy(tpm_class);
class_unregister(&tpm_class);
return rc;
}
@@ -518,8 +515,8 @@ out_destroy_tpm_class:
static void __exit tpm_exit(void)
{
idr_destroy(&dev_nums_idr);
class_destroy(tpm_class);
class_destroy(tpmrm_class);
class_unregister(&tpm_class);
class_unregister(&tpmrm_class);
unregister_chrdev_region(tpm_devt, 2*TPM_NUM_DEVICES);
tpm_dev_common_exit();
}
+2 -2
View File
@@ -230,8 +230,8 @@ enum tpm2_pt_props {
* compiler warnings about stack frame size. */
#define TPM_MAX_RNG_DATA 128
extern struct class *tpm_class;
extern struct class *tpmrm_class;
extern const struct class tpm_class;
extern const struct class tpmrm_class;
extern dev_t tpm_devt;
extern const struct file_operations tpm_fops;
extern const struct file_operations tpmrm_fops;
+1 -1
View File
@@ -606,7 +606,7 @@ int tpm_devs_add(struct tpm_chip *chip)
device_initialize(&chip->devs);
chip->devs.parent = chip->dev.parent;
chip->devs.class = tpmrm_class;
chip->devs.class = &tpmrm_class;
/*
* Get extra reference on main device to hold on behalf of devs.
+1 -1
View File
@@ -29,7 +29,7 @@ config XILLYBUS_PCIE
config XILLYBUS_OF
tristate "Xillybus over Device Tree"
depends on OF && HAS_DMA
depends on OF && HAS_DMA && HAS_IOMEM
help
Set to M if you want Xillybus to find its resources from the
Open Firmware Flattened Device Tree. If the target is an embedded
-1
View File
@@ -10,7 +10,6 @@
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <soc/at91/atmel_tcb.h>
+2 -2
View File
@@ -500,8 +500,8 @@ static int rz_mtu3_count_enable_write(struct counter_device *counter,
int ret = 0;
if (enable) {
pm_runtime_get_sync(ch->dev);
mutex_lock(&priv->lock);
pm_runtime_get_sync(ch->dev);
ret = rz_mtu3_initialize_counter(counter, count->id);
if (ret == 0)
priv->count_is_enabled[count->id] = true;
@@ -510,8 +510,8 @@ static int rz_mtu3_count_enable_write(struct counter_device *counter,
mutex_lock(&priv->lock);
rz_mtu3_terminate_counter(counter, count->id);
priv->count_is_enabled[count->id] = false;
mutex_unlock(&priv->lock);
pm_runtime_put(ch->dev);
mutex_unlock(&priv->lock);
}
return ret;
+1
View File
@@ -62,6 +62,7 @@ config EXTCON_INTEL_CHT_WC
tristate "Intel Cherrytrail Whiskey Cove PMIC extcon driver"
depends on INTEL_SOC_PMIC_CHTWC
depends on USB_SUPPORT
depends on POWER_SUPPLY
select USB_ROLE_SWITCH
help
Say Y here to enable extcon support for charger detection / control
+1 -1
View File
@@ -59,7 +59,7 @@ config GOOGLE_MEMCONSOLE_X86_LEGACY
config GOOGLE_FRAMEBUFFER_COREBOOT
tristate "Coreboot Framebuffer"
depends on FB_SIMPLE
depends on FB_SIMPLE || DRM_SIMPLEDRM
depends on GOOGLE_COREBOOT_TABLE
help
This option enables the kernel to search for a framebuffer in
+99 -1
View File
@@ -33,6 +33,10 @@
#define INVALID_RETRY_COUNTER 0xFF
#define INVALID_DCMF_VERSION 0xFF
#define INVALID_DCMF_STATUS 0xFFFFFFFF
#define INVALID_SPT_ADDRESS 0x0
#define RSU_GET_SPT_CMD 0x5A
#define RSU_GET_SPT_RESP_LEN (4 * sizeof(unsigned int))
typedef void (*rsu_callback)(struct stratix10_svc_client *client,
struct stratix10_svc_cb_data *data);
@@ -58,6 +62,9 @@ typedef void (*rsu_callback)(struct stratix10_svc_client *client,
* @dcmf_status.dcmf3: dcmf3 status
* @retry_counter: the current image's retry counter
* @max_retry: the preset max retry value
* @spt0_address: address of spt0
* @spt1_address: address of spt1
* @get_spt_response_buf: response from sdm for get_spt command
*/
struct stratix10_rsu_priv {
struct stratix10_svc_chan *chan;
@@ -89,6 +96,11 @@ struct stratix10_rsu_priv {
unsigned int retry_counter;
unsigned int max_retry;
unsigned long spt0_address;
unsigned long spt1_address;
unsigned int *get_spt_response_buf;
};
/**
@@ -258,6 +270,36 @@ static void rsu_dcmf_status_callback(struct stratix10_svc_client *client,
complete(&priv->completion);
}
static void rsu_get_spt_callback(struct stratix10_svc_client *client,
struct stratix10_svc_cb_data *data)
{
struct stratix10_rsu_priv *priv = client->priv;
unsigned long *mbox_err = (unsigned long *)data->kaddr1;
unsigned long *resp_len = (unsigned long *)data->kaddr2;
if (data->status != BIT(SVC_STATUS_OK) || (*mbox_err) ||
(*resp_len != RSU_GET_SPT_RESP_LEN))
goto error;
priv->spt0_address = priv->get_spt_response_buf[0];
priv->spt0_address <<= 32;
priv->spt0_address |= priv->get_spt_response_buf[1];
priv->spt1_address = priv->get_spt_response_buf[2];
priv->spt1_address <<= 32;
priv->spt1_address |= priv->get_spt_response_buf[3];
goto complete;
error:
dev_err(client->dev, "failed to get SPTs\n");
complete:
stratix10_svc_free_memory(priv->chan, priv->get_spt_response_buf);
priv->get_spt_response_buf = NULL;
complete(&priv->completion);
}
/**
* rsu_send_msg() - send a message to Intel service layer
* @priv: pointer to rsu private data
@@ -287,6 +329,14 @@ static int rsu_send_msg(struct stratix10_rsu_priv *priv,
if (arg)
msg.arg[0] = arg;
if (command == COMMAND_MBOX_SEND_CMD) {
msg.arg[1] = 0;
msg.payload = NULL;
msg.payload_length = 0;
msg.payload_output = priv->get_spt_response_buf;
msg.payload_length_output = RSU_GET_SPT_RESP_LEN;
}
ret = stratix10_svc_send(priv->chan, &msg);
if (ret < 0)
goto status_done;
@@ -571,6 +621,34 @@ static ssize_t notify_store(struct device *dev,
return count;
}
static ssize_t spt0_address_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct stratix10_rsu_priv *priv = dev_get_drvdata(dev);
if (!priv)
return -ENODEV;
if (priv->spt0_address == INVALID_SPT_ADDRESS)
return -EIO;
return scnprintf(buf, PAGE_SIZE, "0x%08lx\n", priv->spt0_address);
}
static ssize_t spt1_address_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct stratix10_rsu_priv *priv = dev_get_drvdata(dev);
if (!priv)
return -ENODEV;
if (priv->spt1_address == INVALID_SPT_ADDRESS)
return -EIO;
return scnprintf(buf, PAGE_SIZE, "0x%08lx\n", priv->spt1_address);
}
static DEVICE_ATTR_RO(current_image);
static DEVICE_ATTR_RO(fail_image);
static DEVICE_ATTR_RO(state);
@@ -589,6 +667,8 @@ static DEVICE_ATTR_RO(dcmf2_status);
static DEVICE_ATTR_RO(dcmf3_status);
static DEVICE_ATTR_WO(reboot_image);
static DEVICE_ATTR_WO(notify);
static DEVICE_ATTR_RO(spt0_address);
static DEVICE_ATTR_RO(spt1_address);
static struct attribute *rsu_attrs[] = {
&dev_attr_current_image.attr,
@@ -609,6 +689,8 @@ static struct attribute *rsu_attrs[] = {
&dev_attr_dcmf3_status.attr,
&dev_attr_reboot_image.attr,
&dev_attr_notify.attr,
&dev_attr_spt0_address.attr,
&dev_attr_spt1_address.attr,
NULL
};
@@ -638,11 +720,13 @@ static int stratix10_rsu_probe(struct platform_device *pdev)
priv->dcmf_version.dcmf1 = INVALID_DCMF_VERSION;
priv->dcmf_version.dcmf2 = INVALID_DCMF_VERSION;
priv->dcmf_version.dcmf3 = INVALID_DCMF_VERSION;
priv->max_retry = INVALID_RETRY_COUNTER;
priv->dcmf_status.dcmf0 = INVALID_DCMF_STATUS;
priv->dcmf_status.dcmf1 = INVALID_DCMF_STATUS;
priv->dcmf_status.dcmf2 = INVALID_DCMF_STATUS;
priv->dcmf_status.dcmf3 = INVALID_DCMF_STATUS;
priv->max_retry = INVALID_RETRY_COUNTER;
priv->spt0_address = INVALID_SPT_ADDRESS;
priv->spt1_address = INVALID_SPT_ADDRESS;
mutex_init(&priv->lock);
priv->chan = stratix10_svc_request_channel_byname(&priv->client,
@@ -692,6 +776,20 @@ static int stratix10_rsu_probe(struct platform_device *pdev)
stratix10_svc_free_channel(priv->chan);
}
priv->get_spt_response_buf =
stratix10_svc_allocate_memory(priv->chan, RSU_GET_SPT_RESP_LEN);
if (IS_ERR(priv->get_spt_response_buf)) {
dev_err(dev, "failed to allocate get spt buffer\n");
} else {
ret = rsu_send_msg(priv, COMMAND_MBOX_SEND_CMD,
RSU_GET_SPT_CMD, rsu_get_spt_callback);
if (ret) {
dev_err(dev, "Error, getting SPT table %i\n", ret);
stratix10_svc_free_channel(priv->chan);
}
}
return ret;
}
+19 -1
View File
@@ -37,6 +37,7 @@
#define SVC_NUM_CHANNEL 3
#define FPGA_CONFIG_DATA_CLAIM_TIMEOUT_MS 200
#define FPGA_CONFIG_STATUS_TIMEOUT_SEC 30
#define BYTE_TO_WORD_SIZE 4
/* stratix10 service layer clients */
#define STRATIX10_RSU "stratix10-rsu"
@@ -361,6 +362,13 @@ static void svc_thread_recv_status_ok(struct stratix10_svc_data *p_data,
cb_data->kaddr2 = svc_pa_to_va(res.a2);
cb_data->kaddr3 = &res.a3;
break;
case COMMAND_MBOX_SEND_CMD:
cb_data->status = BIT(SVC_STATUS_OK);
cb_data->kaddr1 = &res.a1;
/* SDM return size in u8. Convert size to u32 word */
res.a2 = res.a2 * BYTE_TO_WORD_SIZE;
cb_data->kaddr2 = &res.a2;
break;
default:
pr_warn("it shouldn't happen\n");
break;
@@ -534,6 +542,15 @@ static int svc_normal_to_secure_thread(void *data)
a1 = 0;
a2 = 0;
break;
case COMMAND_MBOX_SEND_CMD:
a0 = INTEL_SIP_SMC_MBOX_SEND_CMD;
a1 = pdata->arg[0];
a2 = (unsigned long)pdata->paddr;
a3 = (unsigned long)pdata->size / BYTE_TO_WORD_SIZE;
a4 = pdata->arg[1];
a5 = (unsigned long)pdata->paddr_output;
a6 = (unsigned long)pdata->size_output / BYTE_TO_WORD_SIZE;
break;
default:
pr_warn("it shouldn't happen\n");
break;
@@ -597,6 +614,7 @@ static int svc_normal_to_secure_thread(void *data)
case COMMAND_FCS_DATA_ENCRYPTION:
case COMMAND_FCS_DATA_DECRYPTION:
case COMMAND_FCS_RANDOM_NUMBER_GEN:
case COMMAND_MBOX_SEND_CMD:
cbdata->status = BIT(SVC_STATUS_INVALID_PARAM);
cbdata->kaddr1 = NULL;
cbdata->kaddr2 = NULL;
@@ -756,7 +774,7 @@ svc_create_memory_pool(struct platform_device *pdev,
paddr = begin;
size = end - begin;
va = devm_memremap(dev, paddr, size, MEMREMAP_WC);
if (!va) {
if (IS_ERR(va)) {
dev_err(dev, "fail to remap shared memory\n");
return ERR_PTR(-EINVAL);
}
+2
View File
@@ -276,4 +276,6 @@ config FPGA_MGR_LATTICE_SYSCONFIG_SPI
FPGA manager driver support for Lattice FPGAs programming over slave
SPI sysCONFIG interface.
source "drivers/fpga/tests/Kconfig"
endif # FPGA
+3
View File
@@ -55,3 +55,6 @@ obj-$(CONFIG_FPGA_DFL_NIOS_INTEL_PAC_N3000) += dfl-n3000-nios.o
# Drivers for FPGAs which implement DFL
obj-$(CONFIG_FPGA_DFL_PCI) += dfl-pci.o
# KUnit tests
obj-$(CONFIG_FPGA_KUNIT_TESTS) += tests/
+1 -1
View File
@@ -27,7 +27,7 @@
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/of.h>
#include <linux/regmap.h>
#define ALT_SDR_CTL_FPGAPORTRST_OFST 0x80
+4 -7
View File
@@ -7,8 +7,9 @@
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/of_device.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/fpga/fpga-bridge.h>
#define FREEZE_CSR_STATUS_OFFSET 0
@@ -198,13 +199,11 @@ static const struct fpga_bridge_ops altera_freeze_br_br_ops = {
.enable_show = altera_freeze_br_enable_show,
};
#ifdef CONFIG_OF
static const struct of_device_id altera_freeze_br_of_match[] = {
{ .compatible = "altr,freeze-bridge-controller", },
{},
};
MODULE_DEVICE_TABLE(of, altera_freeze_br_of_match);
#endif
static int altera_freeze_br_probe(struct platform_device *pdev)
{
@@ -213,14 +212,12 @@ static int altera_freeze_br_probe(struct platform_device *pdev)
void __iomem *base_addr;
struct altera_freeze_br_data *priv;
struct fpga_bridge *br;
struct resource *res;
u32 status, revision;
if (!np)
return -ENODEV;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base_addr = devm_ioremap_resource(dev, res);
base_addr = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base_addr))
return PTR_ERR(base_addr);
@@ -270,7 +267,7 @@ static struct platform_driver altera_freeze_br_driver = {
.remove = altera_freeze_br_remove,
.driver = {
.name = "altera_freeze_br",
.of_match_table = of_match_ptr(altera_freeze_br_of_match),
.of_match_table = altera_freeze_br_of_match,
},
};
+3 -6
View File
@@ -9,19 +9,16 @@
*/
#include <linux/fpga/altera-pr-ip-core.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
static int alt_pr_platform_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
void __iomem *reg_base;
struct resource *res;
/* First mmio base is for register access */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
reg_base = devm_ioremap_resource(dev, res);
reg_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(reg_base))
return PTR_ERR(reg_base);
+9 -8
View File
@@ -19,6 +19,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/uaccess.h>
#include <linux/units.h>
#include <linux/fpga-dfl.h>
#include "dfl.h"
@@ -231,19 +232,19 @@ static int thermal_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
switch (attr) {
case hwmon_temp_input:
v = readq(feature->ioaddr + FME_THERM_RDSENSOR_FMT1);
*val = (long)(FIELD_GET(FPGA_TEMPERATURE, v) * 1000);
*val = (long)(FIELD_GET(FPGA_TEMPERATURE, v) * MILLI);
break;
case hwmon_temp_max:
v = readq(feature->ioaddr + FME_THERM_THRESHOLD);
*val = (long)(FIELD_GET(TEMP_THRESHOLD1, v) * 1000);
*val = (long)(FIELD_GET(TEMP_THRESHOLD1, v) * MILLI);
break;
case hwmon_temp_crit:
v = readq(feature->ioaddr + FME_THERM_THRESHOLD);
*val = (long)(FIELD_GET(TEMP_THRESHOLD2, v) * 1000);
*val = (long)(FIELD_GET(TEMP_THRESHOLD2, v) * MILLI);
break;
case hwmon_temp_emergency:
v = readq(feature->ioaddr + FME_THERM_THRESHOLD);
*val = (long)(FIELD_GET(TRIP_THRESHOLD, v) * 1000);
*val = (long)(FIELD_GET(TRIP_THRESHOLD, v) * MILLI);
break;
case hwmon_temp_max_alarm:
v = readq(feature->ioaddr + FME_THERM_THRESHOLD);
@@ -382,15 +383,15 @@ static int power_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
switch (attr) {
case hwmon_power_input:
v = readq(feature->ioaddr + FME_PWR_STATUS);
*val = (long)(FIELD_GET(PWR_CONSUMED, v) * 1000000);
*val = (long)(FIELD_GET(PWR_CONSUMED, v) * MICRO);
break;
case hwmon_power_max:
v = readq(feature->ioaddr + FME_PWR_THRESHOLD);
*val = (long)(FIELD_GET(PWR_THRESHOLD1, v) * 1000000);
*val = (long)(FIELD_GET(PWR_THRESHOLD1, v) * MICRO);
break;
case hwmon_power_crit:
v = readq(feature->ioaddr + FME_PWR_THRESHOLD);
*val = (long)(FIELD_GET(PWR_THRESHOLD2, v) * 1000000);
*val = (long)(FIELD_GET(PWR_THRESHOLD2, v) * MICRO);
break;
case hwmon_power_max_alarm:
v = readq(feature->ioaddr + FME_PWR_THRESHOLD);
@@ -415,7 +416,7 @@ static int power_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
int ret = 0;
u64 v;
val = clamp_val(val / 1000000, 0, PWR_THRESHOLD_MAX);
val = clamp_val(val / MICRO, 0, PWR_THRESHOLD_MAX);
mutex_lock(&pdata->lock);
+1 -3
View File
@@ -280,7 +280,6 @@ static int fme_mgr_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct fme_mgr_priv *priv;
struct fpga_manager *mgr;
struct resource *res;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -290,8 +289,7 @@ static int fme_mgr_probe(struct platform_device *pdev)
priv->ioaddr = pdata->ioaddr;
if (!priv->ioaddr) {
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->ioaddr = devm_ioremap_resource(dev, res);
priv->ioaddr = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->ioaddr))
return PTR_ERR(priv->ioaddr);
}
+4 -11
View File
@@ -156,19 +156,12 @@ static int *cci_pci_create_irq_table(struct pci_dev *pcidev, unsigned int nvec)
static int find_dfls_by_vsec(struct pci_dev *pcidev, struct dfl_fpga_enum_info *info)
{
u32 bir, offset, vndr_hdr, dfl_cnt, dfl_res;
int dfl_res_off, i, bars, voff = 0;
u32 bir, offset, dfl_cnt, dfl_res;
int dfl_res_off, i, bars, voff;
resource_size_t start, len;
while ((voff = pci_find_next_ext_capability(pcidev, voff, PCI_EXT_CAP_ID_VNDR))) {
vndr_hdr = 0;
pci_read_config_dword(pcidev, voff + PCI_VNDR_HEADER, &vndr_hdr);
if (PCI_VNDR_HEADER_ID(vndr_hdr) == PCI_VSEC_ID_INTEL_DFLS &&
pcidev->vendor == PCI_VENDOR_ID_INTEL)
break;
}
voff = pci_find_vsec_capability(pcidev, PCI_VENDOR_ID_INTEL,
PCI_VSEC_ID_INTEL_DFLS);
if (!voff) {
dev_dbg(&pcidev->dev, "%s no DFL VSEC found\n", __func__);
return -ENODEV;
+24 -24
View File
@@ -14,7 +14,7 @@
#include <linux/spinlock.h>
static DEFINE_IDA(fpga_bridge_ida);
static struct class *fpga_bridge_class;
static const struct class fpga_bridge_class;
/* Lock for adding/removing bridges to linked lists*/
static DEFINE_SPINLOCK(bridge_list_lock);
@@ -87,19 +87,20 @@ err_dev:
/**
* of_fpga_bridge_get - get an exclusive reference to an fpga bridge
*
* @np: node pointer of an FPGA bridge
* @info: fpga image specific information
* @np: node pointer of an FPGA bridge.
* @info: fpga image specific information.
*
* Return fpga_bridge struct if successful.
* Return -EBUSY if someone already has a reference to the bridge.
* Return -ENODEV if @np is not an FPGA Bridge.
* Return:
* * fpga_bridge struct pointer if successful.
* * -EBUSY if someone already has a reference to the bridge.
* * -ENODEV if @np is not an FPGA Bridge or can't take parent driver refcount.
*/
struct fpga_bridge *of_fpga_bridge_get(struct device_node *np,
struct fpga_image_info *info)
{
struct device *dev;
dev = class_find_device_by_of_node(fpga_bridge_class, np);
dev = class_find_device_by_of_node(&fpga_bridge_class, np);
if (!dev)
return ERR_PTR(-ENODEV);
@@ -126,7 +127,7 @@ struct fpga_bridge *fpga_bridge_get(struct device *dev,
{
struct device *bridge_dev;
bridge_dev = class_find_device(fpga_bridge_class, NULL, dev,
bridge_dev = class_find_device(&fpga_bridge_class, NULL, dev,
fpga_bridge_dev_match);
if (!bridge_dev)
return ERR_PTR(-ENODEV);
@@ -155,9 +156,9 @@ EXPORT_SYMBOL_GPL(fpga_bridge_put);
* fpga_bridges_enable - enable bridges in a list
* @bridge_list: list of FPGA bridges
*
* Enable each bridge in the list. If list is empty, do nothing.
* Enable each bridge in the list. If list is empty, do nothing.
*
* Return 0 for success or empty bridge list; return error code otherwise.
* Return: 0 for success or empty bridge list or an error code otherwise.
*/
int fpga_bridges_enable(struct list_head *bridge_list)
{
@@ -179,9 +180,9 @@ EXPORT_SYMBOL_GPL(fpga_bridges_enable);
*
* @bridge_list: list of FPGA bridges
*
* Disable each bridge in the list. If list is empty, do nothing.
* Disable each bridge in the list. If list is empty, do nothing.
*
* Return 0 for success or empty bridge list; return error code otherwise.
* Return: 0 for success or empty bridge list or an error code otherwise.
*/
int fpga_bridges_disable(struct list_head *bridge_list)
{
@@ -230,7 +231,7 @@ EXPORT_SYMBOL_GPL(fpga_bridges_put);
*
* Get an exclusive reference to the bridge and it to the list.
*
* Return 0 for success, error code from of_fpga_bridge_get() otherwise.
* Return: 0 for success, error code from of_fpga_bridge_get() otherwise.
*/
int of_fpga_bridge_get_to_list(struct device_node *np,
struct fpga_image_info *info,
@@ -260,7 +261,7 @@ EXPORT_SYMBOL_GPL(of_fpga_bridge_get_to_list);
*
* Get an exclusive reference to the bridge and it to the list.
*
* Return 0 for success, error code from fpga_bridge_get() otherwise.
* Return: 0 for success, error code from fpga_bridge_get() otherwise.
*/
int fpga_bridge_get_to_list(struct device *dev,
struct fpga_image_info *info,
@@ -359,7 +360,7 @@ fpga_bridge_register(struct device *parent, const char *name,
bridge->priv = priv;
bridge->dev.groups = br_ops->groups;
bridge->dev.class = fpga_bridge_class;
bridge->dev.class = &fpga_bridge_class;
bridge->dev.parent = parent;
bridge->dev.of_node = parent->of_node;
bridge->dev.id = id;
@@ -415,21 +416,20 @@ static void fpga_bridge_dev_release(struct device *dev)
kfree(bridge);
}
static const struct class fpga_bridge_class = {
.name = "fpga_bridge",
.dev_groups = fpga_bridge_groups,
.dev_release = fpga_bridge_dev_release,
};
static int __init fpga_bridge_dev_init(void)
{
fpga_bridge_class = class_create("fpga_bridge");
if (IS_ERR(fpga_bridge_class))
return PTR_ERR(fpga_bridge_class);
fpga_bridge_class->dev_groups = fpga_bridge_groups;
fpga_bridge_class->dev_release = fpga_bridge_dev_release;
return 0;
return class_register(&fpga_bridge_class);
}
static void __exit fpga_bridge_dev_exit(void)
{
class_destroy(fpga_bridge_class);
class_unregister(&fpga_bridge_class);
ida_destroy(&fpga_bridge_ida);
}
+12 -13
View File
@@ -19,7 +19,7 @@
#include <linux/highmem.h>
static DEFINE_IDA(fpga_mgr_ida);
static struct class *fpga_mgr_class;
static const struct class fpga_mgr_class;
struct fpga_mgr_devres {
struct fpga_manager *mgr;
@@ -693,7 +693,7 @@ static int fpga_mgr_dev_match(struct device *dev, const void *data)
*/
struct fpga_manager *fpga_mgr_get(struct device *dev)
{
struct device *mgr_dev = class_find_device(fpga_mgr_class, NULL, dev,
struct device *mgr_dev = class_find_device(&fpga_mgr_class, NULL, dev,
fpga_mgr_dev_match);
if (!mgr_dev)
return ERR_PTR(-ENODEV);
@@ -713,7 +713,7 @@ struct fpga_manager *of_fpga_mgr_get(struct device_node *node)
{
struct device *dev;
dev = class_find_device_by_of_node(fpga_mgr_class, node);
dev = class_find_device_by_of_node(&fpga_mgr_class, node);
if (!dev)
return ERR_PTR(-ENODEV);
@@ -809,7 +809,7 @@ fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *in
mgr->priv = info->priv;
mgr->compat_id = info->compat_id;
mgr->dev.class = fpga_mgr_class;
mgr->dev.class = &fpga_mgr_class;
mgr->dev.groups = mops->groups;
mgr->dev.parent = parent;
mgr->dev.of_node = parent->of_node;
@@ -967,23 +967,22 @@ static void fpga_mgr_dev_release(struct device *dev)
kfree(mgr);
}
static const struct class fpga_mgr_class = {
.name = "fpga_manager",
.dev_groups = fpga_mgr_groups,
.dev_release = fpga_mgr_dev_release,
};
static int __init fpga_mgr_class_init(void)
{
pr_info("FPGA manager framework\n");
fpga_mgr_class = class_create("fpga_manager");
if (IS_ERR(fpga_mgr_class))
return PTR_ERR(fpga_mgr_class);
fpga_mgr_class->dev_groups = fpga_mgr_groups;
fpga_mgr_class->dev_release = fpga_mgr_dev_release;
return 0;
return class_register(&fpga_mgr_class);
}
static void __exit fpga_mgr_class_exit(void)
{
class_destroy(fpga_mgr_class);
class_unregister(&fpga_mgr_class);
ida_destroy(&fpga_mgr_ida);
}
+19 -18
View File
@@ -16,7 +16,7 @@
#include <linux/spinlock.h>
static DEFINE_IDA(fpga_region_ida);
static struct class *fpga_region_class;
static const struct class fpga_region_class;
struct fpga_region *
fpga_region_class_find(struct device *start, const void *data,
@@ -24,7 +24,7 @@ fpga_region_class_find(struct device *start, const void *data,
{
struct device *dev;
dev = class_find_device(fpga_region_class, start, data, match);
dev = class_find_device(&fpga_region_class, start, data, match);
if (!dev)
return NULL;
@@ -38,9 +38,10 @@ EXPORT_SYMBOL_GPL(fpga_region_class_find);
*
* Caller should call fpga_region_put() when done with region.
*
* Return fpga_region struct if successful.
* Return -EBUSY if someone already has a reference to the region.
* Return -ENODEV if @np is not an FPGA Region.
* Return:
* * fpga_region struct if successful.
* * -EBUSY if someone already has a reference to the region.
* * -ENODEV if can't take parent driver module refcount.
*/
static struct fpga_region *fpga_region_get(struct fpga_region *region)
{
@@ -91,7 +92,7 @@ static void fpga_region_put(struct fpga_region *region)
* The caller will need to call fpga_bridges_put() before attempting to
* reprogram the region.
*
* Return 0 for success or negative error code.
* Return: 0 for success or negative error code.
*/
int fpga_region_program_fpga(struct fpga_region *region)
{
@@ -216,7 +217,7 @@ fpga_region_register_full(struct device *parent, const struct fpga_region_info *
mutex_init(&region->mutex);
INIT_LIST_HEAD(&region->bridge_list);
region->dev.class = fpga_region_class;
region->dev.class = &fpga_region_class;
region->dev.parent = parent;
region->dev.of_node = parent->of_node;
region->dev.id = id;
@@ -287,25 +288,25 @@ static void fpga_region_dev_release(struct device *dev)
kfree(region);
}
static const struct class fpga_region_class = {
.name = "fpga_region",
.dev_groups = fpga_region_groups,
.dev_release = fpga_region_dev_release,
};
/**
* fpga_region_init - init function for fpga_region class
* Creates the fpga_region class and registers a reconfig notifier.
* fpga_region_init - creates the fpga_region class.
*
* Return: 0 on success or ERR_PTR() on error.
*/
static int __init fpga_region_init(void)
{
fpga_region_class = class_create("fpga_region");
if (IS_ERR(fpga_region_class))
return PTR_ERR(fpga_region_class);
fpga_region_class->dev_groups = fpga_region_groups;
fpga_region_class->dev_release = fpga_region_dev_release;
return 0;
return class_register(&fpga_region_class);
}
static void __exit fpga_region_exit(void)
{
class_destroy(fpga_region_class);
class_unregister(&fpga_region_class);
ida_destroy(&fpga_region_ida);
}
+1 -1
View File
@@ -8,7 +8,7 @@
#include <linux/fpga/fpga-mgr.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/of.h>
#include <linux/spi/spi.h>
#define MPF_SPI_ISC_ENABLE 0x0B
+2
View File
@@ -12,7 +12,9 @@
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
+2 -5
View File
@@ -471,7 +471,6 @@ static int socfpga_a10_fpga_probe(struct platform_device *pdev)
struct a10_fpga_priv *priv;
void __iomem *reg_base;
struct fpga_manager *mgr;
struct resource *res;
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
@@ -479,14 +478,12 @@ static int socfpga_a10_fpga_probe(struct platform_device *pdev)
return -ENOMEM;
/* First mmio base is for register access */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
reg_base = devm_ioremap_resource(dev, res);
reg_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(reg_base))
return PTR_ERR(reg_base);
/* Second mmio base is for writing FPGA image data */
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
priv->fpga_data_addr = devm_ioremap_resource(dev, res);
priv->fpga_data_addr = devm_platform_ioremap_resource(pdev, 1);
if (IS_ERR(priv->fpga_data_addr))
return PTR_ERR(priv->fpga_data_addr);
+2 -5
View File
@@ -545,20 +545,17 @@ static int socfpga_fpga_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct socfpga_fpga_priv *priv;
struct fpga_manager *mgr;
struct resource *res;
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->fpga_base_addr = devm_ioremap_resource(dev, res);
priv->fpga_base_addr = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->fpga_base_addr))
return PTR_ERR(priv->fpga_base_addr);
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
priv->fpga_data_addr = devm_ioremap_resource(dev, res);
priv->fpga_data_addr = devm_platform_ioremap_resource(pdev, 1);
if (IS_ERR(priv->fpga_data_addr))
return PTR_ERR(priv->fpga_data_addr);
+1
View File
@@ -10,6 +10,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
/*
* FPGA programming requires a higher level of privilege (EL3), per the SoC
+5
View File
@@ -0,0 +1,5 @@
CONFIG_KUNIT=y
CONFIG_FPGA=y
CONFIG_FPGA_REGION=y
CONFIG_FPGA_BRIDGE=y
CONFIG_FPGA_KUNIT_TESTS=y
+11
View File
@@ -0,0 +1,11 @@
config FPGA_KUNIT_TESTS
tristate "KUnit test for the FPGA subsystem" if !KUNIT_ALL_TESTS
depends on FPGA && FPGA_REGION && FPGA_BRIDGE && KUNIT=y
default KUNIT_ALL_TESTS
help
This builds unit tests for the FPGA subsystem
For more information on KUnit and unit tests in general,
please refer to the KUnit documentation in Documentation/dev-tools/kunit/.
If unsure, say N.
+6
View File
@@ -0,0 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for KUnit test suites for the FPGA subsystem
#
obj-$(CONFIG_FPGA_KUNIT_TESTS) += fpga-mgr-test.o fpga-bridge-test.o fpga-region-test.o
+175
View File
@@ -0,0 +1,175 @@
// SPDX-License-Identifier: GPL-2.0
/*
* KUnit test for the FPGA Bridge
*
* Copyright (C) 2023 Red Hat, Inc.
*
* Author: Marco Pagani <marpagan@redhat.com>
*/
#include <kunit/test.h>
#include <linux/device.h>
#include <linux/fpga/fpga-bridge.h>
#include <linux/module.h>
#include <linux/types.h>
struct bridge_stats {
bool enable;
};
struct bridge_ctx {
struct fpga_bridge *bridge;
struct platform_device *pdev;
struct bridge_stats stats;
};
static int op_enable_set(struct fpga_bridge *bridge, bool enable)
{
struct bridge_stats *stats = bridge->priv;
stats->enable = enable;
return 0;
}
/*
* Fake FPGA bridge that implements only the enable_set op to track
* the state.
*/
static const struct fpga_bridge_ops fake_bridge_ops = {
.enable_set = op_enable_set,
};
/**
* register_test_bridge() - Register a fake FPGA bridge for testing.
* @test: KUnit test context object.
*
* Return: Context of the newly registered FPGA bridge.
*/
static struct bridge_ctx *register_test_bridge(struct kunit *test)
{
struct bridge_ctx *ctx;
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
ctx->pdev = platform_device_register_simple("bridge_pdev", PLATFORM_DEVID_AUTO, NULL, 0);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->pdev);
ctx->bridge = fpga_bridge_register(&ctx->pdev->dev, "Fake FPGA bridge", &fake_bridge_ops,
&ctx->stats);
KUNIT_ASSERT_FALSE(test, IS_ERR_OR_NULL(ctx->bridge));
return ctx;
}
static void unregister_test_bridge(struct bridge_ctx *ctx)
{
fpga_bridge_unregister(ctx->bridge);
platform_device_unregister(ctx->pdev);
}
static void fpga_bridge_test_get(struct kunit *test)
{
struct bridge_ctx *ctx = test->priv;
struct fpga_bridge *bridge;
bridge = fpga_bridge_get(&ctx->pdev->dev, NULL);
KUNIT_EXPECT_PTR_EQ(test, bridge, ctx->bridge);
bridge = fpga_bridge_get(&ctx->pdev->dev, NULL);
KUNIT_EXPECT_EQ(test, PTR_ERR(bridge), -EBUSY);
fpga_bridge_put(ctx->bridge);
}
static void fpga_bridge_test_toggle(struct kunit *test)
{
struct bridge_ctx *ctx = test->priv;
int ret;
ret = fpga_bridge_disable(ctx->bridge);
KUNIT_EXPECT_EQ(test, ret, 0);
KUNIT_EXPECT_FALSE(test, ctx->stats.enable);
ret = fpga_bridge_enable(ctx->bridge);
KUNIT_EXPECT_EQ(test, ret, 0);
KUNIT_EXPECT_TRUE(test, ctx->stats.enable);
}
/* Test the functions for getting and controlling a list of bridges */
static void fpga_bridge_test_get_put_list(struct kunit *test)
{
struct list_head bridge_list;
struct bridge_ctx *ctx_0, *ctx_1;
int ret;
ctx_0 = test->priv;
ctx_1 = register_test_bridge(test);
INIT_LIST_HEAD(&bridge_list);
/* Get bridge 0 and add it to the list */
ret = fpga_bridge_get_to_list(&ctx_0->pdev->dev, NULL, &bridge_list);
KUNIT_EXPECT_EQ(test, ret, 0);
KUNIT_EXPECT_PTR_EQ(test, ctx_0->bridge,
list_first_entry_or_null(&bridge_list, struct fpga_bridge, node));
/* Get bridge 1 and add it to the list */
ret = fpga_bridge_get_to_list(&ctx_1->pdev->dev, NULL, &bridge_list);
KUNIT_EXPECT_EQ(test, ret, 0);
KUNIT_EXPECT_PTR_EQ(test, ctx_1->bridge,
list_first_entry_or_null(&bridge_list, struct fpga_bridge, node));
/* Disable an then enable both bridges from the list */
ret = fpga_bridges_disable(&bridge_list);
KUNIT_EXPECT_EQ(test, ret, 0);
KUNIT_EXPECT_FALSE(test, ctx_0->stats.enable);
KUNIT_EXPECT_FALSE(test, ctx_1->stats.enable);
ret = fpga_bridges_enable(&bridge_list);
KUNIT_EXPECT_EQ(test, ret, 0);
KUNIT_EXPECT_TRUE(test, ctx_0->stats.enable);
KUNIT_EXPECT_TRUE(test, ctx_1->stats.enable);
/* Put and remove both bridges from the list */
fpga_bridges_put(&bridge_list);
KUNIT_EXPECT_TRUE(test, list_empty(&bridge_list));
unregister_test_bridge(ctx_1);
}
static int fpga_bridge_test_init(struct kunit *test)
{
test->priv = register_test_bridge(test);
return 0;
}
static void fpga_bridge_test_exit(struct kunit *test)
{
unregister_test_bridge(test->priv);
}
static struct kunit_case fpga_bridge_test_cases[] = {
KUNIT_CASE(fpga_bridge_test_get),
KUNIT_CASE(fpga_bridge_test_toggle),
KUNIT_CASE(fpga_bridge_test_get_put_list),
{}
};
static struct kunit_suite fpga_bridge_suite = {
.name = "fpga_bridge",
.init = fpga_bridge_test_init,
.exit = fpga_bridge_test_exit,
.test_cases = fpga_bridge_test_cases,
};
kunit_test_suite(fpga_bridge_suite);
MODULE_LICENSE("GPL");
+327
View File
@@ -0,0 +1,327 @@
// SPDX-License-Identifier: GPL-2.0
/*
* KUnit test for the FPGA Manager
*
* Copyright (C) 2023 Red Hat, Inc.
*
* Author: Marco Pagani <marpagan@redhat.com>
*/
#include <kunit/test.h>
#include <linux/device.h>
#include <linux/fpga/fpga-mgr.h>
#include <linux/module.h>
#include <linux/scatterlist.h>
#include <linux/types.h>
#define HEADER_FILL 'H'
#define IMAGE_FILL 'P'
#define IMAGE_BLOCK 1024
#define HEADER_SIZE IMAGE_BLOCK
#define IMAGE_SIZE (IMAGE_BLOCK * 4)
struct mgr_stats {
bool header_match;
bool image_match;
u32 seq_num;
u32 op_parse_header_seq;
u32 op_write_init_seq;
u32 op_write_seq;
u32 op_write_sg_seq;
u32 op_write_complete_seq;
enum fpga_mgr_states op_parse_header_state;
enum fpga_mgr_states op_write_init_state;
enum fpga_mgr_states op_write_state;
enum fpga_mgr_states op_write_sg_state;
enum fpga_mgr_states op_write_complete_state;
};
struct mgr_ctx {
struct fpga_image_info *img_info;
struct fpga_manager *mgr;
struct platform_device *pdev;
struct mgr_stats stats;
};
/**
* init_test_buffer() - Allocate and initialize a test image in a buffer.
* @test: KUnit test context object.
* @count: image size in bytes.
*
* Return: pointer to the newly allocated image.
*/
static char *init_test_buffer(struct kunit *test, size_t count)
{
char *buf;
KUNIT_ASSERT_GE(test, count, HEADER_SIZE);
buf = kunit_kzalloc(test, count, GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf);
memset(buf, HEADER_FILL, HEADER_SIZE);
memset(buf + HEADER_SIZE, IMAGE_FILL, count - HEADER_SIZE);
return buf;
}
/*
* Check the image header. Do not return an error code if the image check fails
* since, in this case, it is a failure of the FPGA manager itself, not this
* op that tests it.
*/
static int op_parse_header(struct fpga_manager *mgr, struct fpga_image_info *info,
const char *buf, size_t count)
{
struct mgr_stats *stats = mgr->priv;
size_t i;
stats->op_parse_header_state = mgr->state;
stats->op_parse_header_seq = stats->seq_num++;
/* Set header_size and data_size for later */
info->header_size = HEADER_SIZE;
info->data_size = info->count - HEADER_SIZE;
stats->header_match = true;
for (i = 0; i < info->header_size; i++) {
if (buf[i] != HEADER_FILL) {
stats->header_match = false;
break;
}
}
return 0;
}
static int op_write_init(struct fpga_manager *mgr, struct fpga_image_info *info,
const char *buf, size_t count)
{
struct mgr_stats *stats = mgr->priv;
stats->op_write_init_state = mgr->state;
stats->op_write_init_seq = stats->seq_num++;
return 0;
}
/*
* Check the image data. As with op_parse_header, do not return an error code
* if the image check fails.
*/
static int op_write(struct fpga_manager *mgr, const char *buf, size_t count)
{
struct mgr_stats *stats = mgr->priv;
size_t i;
stats->op_write_state = mgr->state;
stats->op_write_seq = stats->seq_num++;
stats->image_match = true;
for (i = 0; i < count; i++) {
if (buf[i] != IMAGE_FILL) {
stats->image_match = false;
break;
}
}
return 0;
}
/*
* Check the image data, but first skip the header since write_sg will get
* the whole image in sg_table. As with op_parse_header, do not return an
* error code if the image check fails.
*/
static int op_write_sg(struct fpga_manager *mgr, struct sg_table *sgt)
{
struct mgr_stats *stats = mgr->priv;
struct sg_mapping_iter miter;
char *img;
size_t i;
stats->op_write_sg_state = mgr->state;
stats->op_write_sg_seq = stats->seq_num++;
stats->image_match = true;
sg_miter_start(&miter, sgt->sgl, sgt->nents, SG_MITER_FROM_SG);
if (!sg_miter_skip(&miter, HEADER_SIZE)) {
stats->image_match = false;
goto out;
}
while (sg_miter_next(&miter)) {
img = miter.addr;
for (i = 0; i < miter.length; i++) {
if (img[i] != IMAGE_FILL) {
stats->image_match = false;
goto out;
}
}
}
out:
sg_miter_stop(&miter);
return 0;
}
static int op_write_complete(struct fpga_manager *mgr, struct fpga_image_info *info)
{
struct mgr_stats *stats = mgr->priv;
stats->op_write_complete_state = mgr->state;
stats->op_write_complete_seq = stats->seq_num++;
return 0;
}
/*
* Fake FPGA manager that implements all ops required to check the programming
* sequence using a single contiguous buffer and a scatter gather table.
*/
static const struct fpga_manager_ops fake_mgr_ops = {
.skip_header = true,
.parse_header = op_parse_header,
.write_init = op_write_init,
.write = op_write,
.write_sg = op_write_sg,
.write_complete = op_write_complete,
};
static void fpga_mgr_test_get(struct kunit *test)
{
struct mgr_ctx *ctx = test->priv;
struct fpga_manager *mgr;
mgr = fpga_mgr_get(&ctx->pdev->dev);
KUNIT_EXPECT_PTR_EQ(test, mgr, ctx->mgr);
fpga_mgr_put(ctx->mgr);
}
static void fpga_mgr_test_lock(struct kunit *test)
{
struct mgr_ctx *ctx = test->priv;
int ret;
ret = fpga_mgr_lock(ctx->mgr);
KUNIT_EXPECT_EQ(test, ret, 0);
ret = fpga_mgr_lock(ctx->mgr);
KUNIT_EXPECT_EQ(test, ret, -EBUSY);
fpga_mgr_unlock(ctx->mgr);
}
/* Check the programming sequence using an image in a buffer */
static void fpga_mgr_test_img_load_buf(struct kunit *test)
{
struct mgr_ctx *ctx = test->priv;
char *img_buf;
int ret;
img_buf = init_test_buffer(test, IMAGE_SIZE);
ctx->img_info->count = IMAGE_SIZE;
ctx->img_info->buf = img_buf;
ret = fpga_mgr_load(ctx->mgr, ctx->img_info);
KUNIT_EXPECT_EQ(test, ret, 0);
KUNIT_EXPECT_TRUE(test, ctx->stats.header_match);
KUNIT_EXPECT_TRUE(test, ctx->stats.image_match);
KUNIT_EXPECT_EQ(test, ctx->stats.op_parse_header_state, FPGA_MGR_STATE_PARSE_HEADER);
KUNIT_EXPECT_EQ(test, ctx->stats.op_write_init_state, FPGA_MGR_STATE_WRITE_INIT);
KUNIT_EXPECT_EQ(test, ctx->stats.op_write_state, FPGA_MGR_STATE_WRITE);
KUNIT_EXPECT_EQ(test, ctx->stats.op_write_complete_state, FPGA_MGR_STATE_WRITE_COMPLETE);
KUNIT_EXPECT_EQ(test, ctx->stats.op_write_init_seq, ctx->stats.op_parse_header_seq + 1);
KUNIT_EXPECT_EQ(test, ctx->stats.op_write_seq, ctx->stats.op_parse_header_seq + 2);
KUNIT_EXPECT_EQ(test, ctx->stats.op_write_complete_seq, ctx->stats.op_parse_header_seq + 3);
}
/* Check the programming sequence using an image in a scatter gather table */
static void fpga_mgr_test_img_load_sgt(struct kunit *test)
{
struct mgr_ctx *ctx = test->priv;
struct sg_table *sgt;
char *img_buf;
int ret;
img_buf = init_test_buffer(test, IMAGE_SIZE);
sgt = kunit_kzalloc(test, sizeof(*sgt), GFP_KERNEL);
ret = sg_alloc_table(sgt, 1, GFP_KERNEL);
KUNIT_ASSERT_EQ(test, ret, 0);
sg_init_one(sgt->sgl, img_buf, IMAGE_SIZE);
ctx->img_info->sgt = sgt;
ret = fpga_mgr_load(ctx->mgr, ctx->img_info);
KUNIT_EXPECT_EQ(test, ret, 0);
KUNIT_EXPECT_TRUE(test, ctx->stats.header_match);
KUNIT_EXPECT_TRUE(test, ctx->stats.image_match);
KUNIT_EXPECT_EQ(test, ctx->stats.op_parse_header_state, FPGA_MGR_STATE_PARSE_HEADER);
KUNIT_EXPECT_EQ(test, ctx->stats.op_write_init_state, FPGA_MGR_STATE_WRITE_INIT);
KUNIT_EXPECT_EQ(test, ctx->stats.op_write_sg_state, FPGA_MGR_STATE_WRITE);
KUNIT_EXPECT_EQ(test, ctx->stats.op_write_complete_state, FPGA_MGR_STATE_WRITE_COMPLETE);
KUNIT_EXPECT_EQ(test, ctx->stats.op_write_init_seq, ctx->stats.op_parse_header_seq + 1);
KUNIT_EXPECT_EQ(test, ctx->stats.op_write_sg_seq, ctx->stats.op_parse_header_seq + 2);
KUNIT_EXPECT_EQ(test, ctx->stats.op_write_complete_seq, ctx->stats.op_parse_header_seq + 3);
sg_free_table(ctx->img_info->sgt);
}
static int fpga_mgr_test_init(struct kunit *test)
{
struct mgr_ctx *ctx;
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
ctx->pdev = platform_device_register_simple("mgr_pdev", PLATFORM_DEVID_AUTO, NULL, 0);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->pdev);
ctx->mgr = devm_fpga_mgr_register(&ctx->pdev->dev, "Fake FPGA Manager", &fake_mgr_ops,
&ctx->stats);
KUNIT_ASSERT_FALSE(test, IS_ERR_OR_NULL(ctx->mgr));
ctx->img_info = fpga_image_info_alloc(&ctx->pdev->dev);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->img_info);
test->priv = ctx;
return 0;
}
static void fpga_mgr_test_exit(struct kunit *test)
{
struct mgr_ctx *ctx = test->priv;
fpga_image_info_free(ctx->img_info);
platform_device_unregister(ctx->pdev);
}
static struct kunit_case fpga_mgr_test_cases[] = {
KUNIT_CASE(fpga_mgr_test_get),
KUNIT_CASE(fpga_mgr_test_lock),
KUNIT_CASE(fpga_mgr_test_img_load_buf),
KUNIT_CASE(fpga_mgr_test_img_load_sgt),
{}
};
static struct kunit_suite fpga_mgr_suite = {
.name = "fpga_mgr",
.init = fpga_mgr_test_init,
.exit = fpga_mgr_test_exit,
.test_cases = fpga_mgr_test_cases,
};
kunit_test_suite(fpga_mgr_suite);
MODULE_LICENSE("GPL");
+211
View File
@@ -0,0 +1,211 @@
// SPDX-License-Identifier: GPL-2.0
/*
* KUnit test for the FPGA Region
*
* Copyright (C) 2023 Red Hat, Inc.
*
* Author: Marco Pagani <marpagan@redhat.com>
*/
#include <kunit/test.h>
#include <linux/fpga/fpga-bridge.h>
#include <linux/fpga/fpga-mgr.h>
#include <linux/fpga/fpga-region.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/types.h>
struct mgr_stats {
u32 write_count;
};
struct bridge_stats {
bool enable;
u32 cycles_count;
};
struct test_ctx {
struct fpga_manager *mgr;
struct platform_device *mgr_pdev;
struct fpga_bridge *bridge;
struct platform_device *bridge_pdev;
struct fpga_region *region;
struct platform_device *region_pdev;
struct bridge_stats bridge_stats;
struct mgr_stats mgr_stats;
};
static int op_write(struct fpga_manager *mgr, const char *buf, size_t count)
{
struct mgr_stats *stats = mgr->priv;
stats->write_count++;
return 0;
}
/*
* Fake FPGA manager that implements only the write op to count the number
* of programming cycles. The internals of the programming sequence are
* tested in the Manager suite since they are outside the responsibility
* of the Region.
*/
static const struct fpga_manager_ops fake_mgr_ops = {
.write = op_write,
};
static int op_enable_set(struct fpga_bridge *bridge, bool enable)
{
struct bridge_stats *stats = bridge->priv;
if (!stats->enable && enable)
stats->cycles_count++;
stats->enable = enable;
return 0;
}
/*
* Fake FPGA bridge that implements only enable_set op to count the number
* of activation cycles.
*/
static const struct fpga_bridge_ops fake_bridge_ops = {
.enable_set = op_enable_set,
};
static int fake_region_get_bridges(struct fpga_region *region)
{
struct fpga_bridge *bridge = region->priv;
return fpga_bridge_get_to_list(bridge->dev.parent, region->info, &region->bridge_list);
}
static int fake_region_match(struct device *dev, const void *data)
{
return dev->parent == data;
}
static void fpga_region_test_class_find(struct kunit *test)
{
struct test_ctx *ctx = test->priv;
struct fpga_region *region;
region = fpga_region_class_find(NULL, &ctx->region_pdev->dev, fake_region_match);
KUNIT_EXPECT_PTR_EQ(test, region, ctx->region);
}
/*
* FPGA Region programming test. The Region must call get_bridges() to get
* and control the bridges, and then the Manager for the actual programming.
*/
static void fpga_region_test_program_fpga(struct kunit *test)
{
struct test_ctx *ctx = test->priv;
struct fpga_image_info *img_info;
char img_buf[4];
int ret;
img_info = fpga_image_info_alloc(&ctx->mgr_pdev->dev);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, img_info);
img_info->buf = img_buf;
img_info->count = sizeof(img_buf);
ctx->region->info = img_info;
ret = fpga_region_program_fpga(ctx->region);
KUNIT_ASSERT_EQ(test, ret, 0);
KUNIT_EXPECT_EQ(test, 1, ctx->mgr_stats.write_count);
KUNIT_EXPECT_EQ(test, 1, ctx->bridge_stats.cycles_count);
fpga_bridges_put(&ctx->region->bridge_list);
ret = fpga_region_program_fpga(ctx->region);
KUNIT_ASSERT_EQ(test, ret, 0);
KUNIT_EXPECT_EQ(test, 2, ctx->mgr_stats.write_count);
KUNIT_EXPECT_EQ(test, 2, ctx->bridge_stats.cycles_count);
fpga_bridges_put(&ctx->region->bridge_list);
fpga_image_info_free(img_info);
}
/*
* The configuration used in this test suite uses a single bridge to
* limit the code under test to a single unit. The functions used by the
* Region for getting and controlling bridges are tested (with a list of
* multiple bridges) in the Bridge suite.
*/
static int fpga_region_test_init(struct kunit *test)
{
struct test_ctx *ctx;
struct fpga_region_info region_info = { 0 };
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
ctx->mgr_pdev = platform_device_register_simple("mgr_pdev", PLATFORM_DEVID_AUTO, NULL, 0);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->mgr_pdev);
ctx->mgr = devm_fpga_mgr_register(&ctx->mgr_pdev->dev, "Fake FPGA Manager", &fake_mgr_ops,
&ctx->mgr_stats);
KUNIT_ASSERT_FALSE(test, IS_ERR_OR_NULL(ctx->mgr));
ctx->bridge_pdev = platform_device_register_simple("bridge_pdev", PLATFORM_DEVID_AUTO,
NULL, 0);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->bridge_pdev);
ctx->bridge = fpga_bridge_register(&ctx->bridge_pdev->dev, "Fake FPGA Bridge",
&fake_bridge_ops, &ctx->bridge_stats);
KUNIT_ASSERT_FALSE(test, IS_ERR_OR_NULL(ctx->bridge));
ctx->bridge_stats.enable = true;
ctx->region_pdev = platform_device_register_simple("region_pdev", PLATFORM_DEVID_AUTO,
NULL, 0);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->region_pdev);
region_info.mgr = ctx->mgr;
region_info.priv = ctx->bridge;
region_info.get_bridges = fake_region_get_bridges;
ctx->region = fpga_region_register_full(&ctx->region_pdev->dev, &region_info);
KUNIT_ASSERT_FALSE(test, IS_ERR_OR_NULL(ctx->region));
test->priv = ctx;
return 0;
}
static void fpga_region_test_exit(struct kunit *test)
{
struct test_ctx *ctx = test->priv;
fpga_region_unregister(ctx->region);
platform_device_unregister(ctx->region_pdev);
fpga_bridge_unregister(ctx->bridge);
platform_device_unregister(ctx->bridge_pdev);
platform_device_unregister(ctx->mgr_pdev);
}
static struct kunit_case fpga_region_test_cases[] = {
KUNIT_CASE(fpga_region_test_class_find),
KUNIT_CASE(fpga_region_test_program_fpga),
{}
};
static struct kunit_suite fpga_region_suite = {
.name = "fpga_mgr",
.init = fpga_region_test_init,
.exit = fpga_region_test_exit,
.test_cases = fpga_region_test_cases,
};
kunit_test_suite(fpga_region_suite);
MODULE_LICENSE("GPL");
+1 -3
View File
@@ -103,7 +103,6 @@ static int ts73xx_fpga_probe(struct platform_device *pdev)
struct device *kdev = &pdev->dev;
struct ts73xx_fpga_priv *priv;
struct fpga_manager *mgr;
struct resource *res;
priv = devm_kzalloc(kdev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -111,8 +110,7 @@ static int ts73xx_fpga_probe(struct platform_device *pdev)
priv->dev = kdev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->io_base = devm_ioremap_resource(kdev, res);
priv->io_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->io_base))
return PTR_ERR(priv->io_base);
+1 -3
View File
@@ -108,7 +108,6 @@ static int xlnx_pr_decoupler_probe(struct platform_device *pdev)
struct xlnx_pr_decoupler_data *priv;
struct fpga_bridge *br;
int err;
struct resource *res;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -122,8 +121,7 @@ static int xlnx_pr_decoupler_probe(struct platform_device *pdev)
priv->ipconfig = match->data;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->io_base = devm_ioremap_resource(&pdev->dev, res);
priv->io_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->io_base))
return PTR_ERR(priv->io_base);
+1 -3
View File
@@ -555,7 +555,6 @@ static int zynq_fpga_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct zynq_fpga_priv *priv;
struct fpga_manager *mgr;
struct resource *res;
int err;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
@@ -563,8 +562,7 @@ static int zynq_fpga_probe(struct platform_device *pdev)
return -ENOMEM;
spin_lock_init(&priv->dma_lock);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->io_base = devm_ioremap_resource(dev, res);
priv->io_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->io_base))
return PTR_ERR(priv->io_base);
+17
View File
@@ -62,6 +62,15 @@ config FSI_MASTER_ASPEED
Enable it for your BMC kernel in an OpenPower or IBM Power system.
config FSI_MASTER_I2CR
tristate "IBM I2C Responder virtual FSI master"
depends on I2C
help
This option enables a virtual FSI master in order to access a CFAM
behind an IBM I2C Responder (I2CR) chip. The I2CR is an I2C device
that translates I2C commands to CFAM or SCOM operations, effectively
implementing an FSI master and bus.
config FSI_SCOM
tristate "SCOM FSI client device driver"
help
@@ -85,4 +94,12 @@ config FSI_OCC
provide the raw sensor data as well as perform thermal and power
management on the system.
config I2CR_SCOM
tristate "IBM I2C Responder SCOM driver"
depends on FSI_MASTER_I2CR
help
This option enables an I2C Responder based SCOM device driver. The
I2CR has the capability to directly perform SCOM operations instead
of using the FSI2PIB engine.
endif
+2
View File
@@ -4,7 +4,9 @@ obj-$(CONFIG_FSI) += fsi-core.o
obj-$(CONFIG_FSI_MASTER_HUB) += fsi-master-hub.o
obj-$(CONFIG_FSI_MASTER_ASPEED) += fsi-master-aspeed.o
obj-$(CONFIG_FSI_MASTER_GPIO) += fsi-master-gpio.o
obj-$(CONFIG_FSI_MASTER_I2CR) += fsi-master-i2cr.o
obj-$(CONFIG_FSI_MASTER_AST_CF) += fsi-master-ast-cf.o
obj-$(CONFIG_FSI_SCOM) += fsi-scom.o
obj-$(CONFIG_FSI_SBEFIFO) += fsi-sbefifo.o
obj-$(CONFIG_FSI_OCC) += fsi-occ.o
obj-$(CONFIG_I2CR_SCOM) += i2cr-scom.o
+84 -70
View File
@@ -16,6 +16,8 @@
#include <linux/idr.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/slab.h>
#include <linux/bitops.h>
#include <linux/cdev.h>
@@ -23,6 +25,10 @@
#include <linux/uaccess.h>
#include "fsi-master.h"
#include "fsi-slave.h"
#define CREATE_TRACE_POINTS
#include <trace/events/fsi.h>
#define FSI_SLAVE_CONF_NEXT_MASK GENMASK(31, 31)
#define FSI_SLAVE_CONF_SLOTS_MASK GENMASK(23, 16)
@@ -78,26 +84,6 @@ static const int engine_page_size = 0x400;
static DEFINE_IDA(master_ida);
struct fsi_slave {
struct device dev;
struct fsi_master *master;
struct cdev cdev;
int cdev_idx;
int id; /* FSI address */
int link; /* FSI link# */
u32 cfam_id;
int chip_id;
uint32_t size; /* size of slave address space */
u8 t_send_delay;
u8 t_echo_delay;
};
#define CREATE_TRACE_POINTS
#include <trace/events/fsi.h>
#define to_fsi_master(d) container_of(d, struct fsi_master, dev)
#define to_fsi_slave(d) container_of(d, struct fsi_slave, dev)
static const int slave_retries = 2;
static int discard_errors;
@@ -415,28 +401,18 @@ EXPORT_SYMBOL_GPL(fsi_slave_release_range);
static bool fsi_device_node_matches(struct device *dev, struct device_node *np,
uint32_t addr, uint32_t size)
{
unsigned int len, na, ns;
const __be32 *prop;
uint32_t psize;
u64 paddr, psize;
na = of_n_addr_cells(np);
ns = of_n_size_cells(np);
if (na != 1 || ns != 1)
if (of_property_read_reg(np, 0, &paddr, &psize))
return false;
prop = of_get_property(np, "reg", &len);
if (!prop || len != 8)
if (paddr != addr)
return false;
if (of_read_number(prop, 1) != addr)
return false;
psize = of_read_number(prop + 1, 1);
if (psize != size) {
dev_warn(dev,
"node %s matches probed address, but not size (got 0x%x, expected 0x%x)",
of_node_full_name(np), psize, size);
"node %pOF matches probed address, but not size (got 0x%llx, expected 0x%x)",
np, psize, size);
}
return true;
@@ -653,24 +629,12 @@ static void fsi_slave_release(struct device *dev)
static bool fsi_slave_node_matches(struct device_node *np,
int link, uint8_t id)
{
unsigned int len, na, ns;
const __be32 *prop;
u64 addr;
na = of_n_addr_cells(np);
ns = of_n_size_cells(np);
/* Ensure we have the correct format for addresses and sizes in
* reg properties
*/
if (na != 2 || ns != 0)
if (of_property_read_reg(np, 0, &addr, NULL))
return false;
prop = of_get_property(np, "reg", &len);
if (!prop || len != 8)
return false;
return (of_read_number(prop, 1) == link) &&
(of_read_number(prop + 1, 1) == id);
return addr == (((u64)link << 32) | id);
}
/* Find a matching node for the slave at (link, id). Returns NULL if none
@@ -949,9 +913,13 @@ static int __fsi_get_new_minor(struct fsi_slave *slave, enum fsi_dev_type type,
/* Check if we qualify for legacy numbering */
if (cid >= 0 && cid < 16 && type < 4) {
/* Try reserving the legacy number */
id = (cid << 4) | type;
id = ida_simple_get(&fsi_minor_ida, id, id + 1, GFP_KERNEL);
/*
* Try reserving the legacy number, which has 0 - 0x3f reserved
* in the ida range. cid goes up to 0xf and type contains two
* bits, so construct the id with the below two bit shift.
*/
id = (cid << 2) | type;
id = ida_alloc_range(&fsi_minor_ida, id, id, GFP_KERNEL);
if (id >= 0) {
*out_index = fsi_adjust_index(cid);
*out_dev = fsi_base_dev + id;
@@ -962,8 +930,8 @@ static int __fsi_get_new_minor(struct fsi_slave *slave, enum fsi_dev_type type,
return id;
/* Fallback to non-legacy allocation */
}
id = ida_simple_get(&fsi_minor_ida, FSI_CHAR_LEGACY_TOP,
FSI_CHAR_MAX_DEVICES, GFP_KERNEL);
id = ida_alloc_range(&fsi_minor_ida, FSI_CHAR_LEGACY_TOP,
FSI_CHAR_MAX_DEVICES - 1, GFP_KERNEL);
if (id < 0)
return id;
*out_index = fsi_adjust_index(id);
@@ -971,16 +939,42 @@ static int __fsi_get_new_minor(struct fsi_slave *slave, enum fsi_dev_type type,
return 0;
}
static const char *const fsi_dev_type_names[] = {
"cfam",
"sbefifo",
"scom",
"occ",
};
int fsi_get_new_minor(struct fsi_device *fdev, enum fsi_dev_type type,
dev_t *out_dev, int *out_index)
{
if (fdev->dev.of_node) {
int aid = of_alias_get_id(fdev->dev.of_node, fsi_dev_type_names[type]);
if (aid >= 0) {
/* Use the same scheme as the legacy numbers. */
int id = (aid << 2) | type;
id = ida_alloc_range(&fsi_minor_ida, id, id, GFP_KERNEL);
if (id >= 0) {
*out_index = aid;
*out_dev = fsi_base_dev + id;
return 0;
}
if (id != -ENOSPC)
return id;
}
}
return __fsi_get_new_minor(fdev->slave, type, out_dev, out_index);
}
EXPORT_SYMBOL_GPL(fsi_get_new_minor);
void fsi_free_minor(dev_t dev)
{
ida_simple_remove(&fsi_minor_ida, MINOR(dev));
ida_free(&fsi_minor_ida, MINOR(dev));
}
EXPORT_SYMBOL_GPL(fsi_free_minor);
@@ -1210,6 +1204,7 @@ static int fsi_master_scan(struct fsi_master *master)
{
int link, rc;
trace_fsi_master_scan(master, true);
for (link = 0; link < master->n_links; link++) {
rc = fsi_master_link_enable(master, link);
if (rc) {
@@ -1251,6 +1246,7 @@ static int fsi_master_remove_slave(struct device *dev, void *arg)
static void fsi_master_unscan(struct fsi_master *master)
{
trace_fsi_master_scan(master, false);
device_for_each_child(&master->dev, NULL, fsi_master_remove_slave);
}
@@ -1313,41 +1309,53 @@ int fsi_master_register(struct fsi_master *master)
struct device_node *np;
mutex_init(&master->scan_lock);
master->idx = ida_simple_get(&master_ida, 0, INT_MAX, GFP_KERNEL);
/* Alloc the requested index if it's non-zero */
if (master->idx) {
master->idx = ida_alloc_range(&master_ida, master->idx,
master->idx, GFP_KERNEL);
} else {
master->idx = ida_alloc(&master_ida, GFP_KERNEL);
}
if (master->idx < 0)
return master->idx;
dev_set_name(&master->dev, "fsi%d", master->idx);
if (!dev_name(&master->dev))
dev_set_name(&master->dev, "fsi%d", master->idx);
master->dev.class = &fsi_master_class;
mutex_lock(&master->scan_lock);
rc = device_register(&master->dev);
if (rc) {
ida_simple_remove(&master_ida, master->idx);
return rc;
ida_free(&master_ida, master->idx);
goto out;
}
np = dev_of_node(&master->dev);
if (!of_property_read_bool(np, "no-scan-on-init")) {
mutex_lock(&master->scan_lock);
fsi_master_scan(master);
mutex_unlock(&master->scan_lock);
}
return 0;
out:
mutex_unlock(&master->scan_lock);
return rc;
}
EXPORT_SYMBOL_GPL(fsi_master_register);
void fsi_master_unregister(struct fsi_master *master)
{
if (master->idx >= 0) {
ida_simple_remove(&master_ida, master->idx);
master->idx = -1;
}
int idx = master->idx;
trace_fsi_master_unregister(master);
mutex_lock(&master->scan_lock);
fsi_master_unscan(master);
master->n_links = 0;
mutex_unlock(&master->scan_lock);
device_unregister(&master->dev);
ida_free(&master_ida, idx);
}
EXPORT_SYMBOL_GPL(fsi_master_unregister);
@@ -1366,8 +1374,14 @@ static int fsi_bus_match(struct device *dev, struct device_driver *drv)
if (id->engine_type != fsi_dev->engine_type)
continue;
if (id->version == FSI_VERSION_ANY ||
id->version == fsi_dev->version)
return 1;
id->version == fsi_dev->version) {
if (drv->of_match_table) {
if (of_driver_match_device(dev, drv))
return 1;
} else {
return 1;
}
}
}
return 0;
+3 -1
View File
@@ -376,7 +376,7 @@ static int aspeed_master_break(struct fsi_master *master, int link)
static void aspeed_master_release(struct device *dev)
{
struct fsi_master_aspeed *aspeed =
to_fsi_master_aspeed(dev_to_fsi_master(dev));
to_fsi_master_aspeed(to_fsi_master(dev));
kfree(aspeed);
}
@@ -454,6 +454,8 @@ static ssize_t cfam_reset_store(struct device *dev, struct device_attribute *att
gpiod_set_value(aspeed->cfam_reset_gpio, 1);
usleep_range(900, 1000);
gpiod_set_value(aspeed->cfam_reset_gpio, 0);
usleep_range(900, 1000);
opb_writel(aspeed, ctrl_base + FSI_MRESP0, cpu_to_be32(FSI_MRESP_RST_ALL_MASTER));
mutex_unlock(&aspeed->lock);
trace_fsi_master_aspeed_cfam_reset(false);

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