Char/Misc driver changes for 6.6-rc1

Here is the big set of char/misc and other small driver subsystem
 changes for 6.6-rc1.
 
 Stuff all over the place here, lots of driver updates and changes and
 new additions.  Short summary is:
   - new IIO drivers and updates
   - Interconnect driver updates
   - fpga driver updates and additions
   - fsi driver updates
   - mei driver updates
   - coresight driver updates
   - nvmem driver updates
   - counter driver updates
   - lots of smaller misc and char driver updates and additions
 
 All of these have been in linux-next for a long time with no reported
 problems.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCZPH64g8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+ynr2QCfd3RKeR+WnGzyEOFhksl30UJJhiIAoNZtYT5+
 t9KG0iMDXRuTsOqeEQbd
 =tVnk
 -----END PGP SIGNATURE-----

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

Pull char/misc driver updates from Greg KH:
 "Here is the big set of char/misc and other small driver subsystem
  changes for 6.6-rc1.

  Stuff all over the place here, lots of driver updates and changes and
  new additions. Short summary is:

   - new IIO drivers and updates

   - Interconnect driver updates

   - fpga driver updates and additions

   - fsi driver updates

   - mei driver updates

   - coresight driver updates

   - nvmem driver updates

   - counter driver updates

   - lots of smaller misc and char driver updates and additions

  All of these have been in linux-next for a long time with no reported
  problems"

* tag 'char-misc-6.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (267 commits)
  nvmem: core: Notify when a new layout is registered
  nvmem: core: Do not open-code existing functions
  nvmem: core: Return NULL when no nvmem layout is found
  nvmem: core: Create all cells before adding the nvmem device
  nvmem: u-boot-env:: Replace zero-length array with DECLARE_FLEX_ARRAY() helper
  nvmem: sec-qfprom: Add Qualcomm secure QFPROM support
  dt-bindings: nvmem: sec-qfprom: Add bindings for secure qfprom
  dt-bindings: nvmem: Add compatible for QCM2290
  nvmem: Kconfig: Fix typo "drive" -> "driver"
  nvmem: Explicitly include correct DT includes
  nvmem: add new NXP QorIQ eFuse driver
  dt-bindings: nvmem: Add t1023-sfp efuse support
  dt-bindings: nvmem: qfprom: Add compatible for MSM8226
  nvmem: uniphier: Use devm_platform_get_and_ioremap_resource()
  nvmem: qfprom: do some cleanup
  nvmem: stm32-romem: Use devm_platform_get_and_ioremap_resource()
  nvmem: rockchip-efuse: Use devm_platform_get_and_ioremap_resource()
  nvmem: meson-mx-efuse: Convert to devm_platform_ioremap_resource()
  nvmem: lpc18xx_otp: Convert to devm_platform_ioremap_resource()
  nvmem: brcm_nvram: Use devm_platform_get_and_ioremap_resource()
  ...
This commit is contained in:
Linus Torvalds 2023-09-01 09:53:54 -07:00
commit 1c9f8dff62
313 changed files with 20475 additions and 2858 deletions

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

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

View File

@ -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.

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.

View File

@ -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:

View File

@ -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

View File

@ -23,6 +23,7 @@ properties:
connector:
$ref: /schemas/connector/usb-connector.yaml#
unevaluatedProperties: false
ports:
$ref: /schemas/graph.yaml#/properties/ports

View File

@ -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

View File

@ -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>;
};
};

View File

@ -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>;
};
};
...

View File

@ -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

View File

@ -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>;
};
};

View File

@ -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;
};

View File

@ -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#

View File

@ -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>;
};
};

View File

@ -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>;
};
};
...

View File

@ -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:
- |

View File

@ -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:
- |

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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>;
};
...

View File

@ -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

View File

@ -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>;
};

View File

@ -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

View File

@ -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

View File

@ -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>;
};
};
};

View File

@ -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>;
};
...

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

View File

@ -14007,12 +14007,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>

View File

@ -220,6 +220,15 @@ kcs3: kcs3@0 {
};
};
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>;

View File

@ -68,6 +68,15 @@ apb {
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>;

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)

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 */

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);

View File

@ -6557,6 +6557,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)
@ -6566,19 +6567,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) {

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>

View File

@ -365,12 +365,10 @@ int mhi_alloc_bhie_table(struct mhi_controller *mhi_cntrl,
}
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);

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;

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:

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 },

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;

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>

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>

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>

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>

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

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>

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;

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

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

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;
}

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);
}

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

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/

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

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,
},
};

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);

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);

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);
}

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;

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 @@ static struct fpga_bridge *__fpga_bridge_get(struct device *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);
}

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);
}

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);
}

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

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>

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);

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);

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

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

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.

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

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");

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");

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");

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);

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);

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);

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

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

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;

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);

View File

@ -1133,7 +1133,7 @@ static int fsi_master_acf_gpio_request(void *data)
/* Note: This doesn't require holding out mutex */
/* Write reqest */
/* Write request */
iowrite8(ARB_ARM_REQ, master->sram + ARB_REG);
/*
@ -1190,7 +1190,7 @@ static int fsi_master_acf_gpio_release(void *data)
static void fsi_master_acf_release(struct device *dev)
{
struct fsi_master_acf *master = to_fsi_master_acf(dev_to_fsi_master(dev));
struct fsi_master_acf *master = to_fsi_master_acf(to_fsi_master(dev));
/* Cleanup, stop coprocessor */
mutex_lock(&master->lock);
@ -1441,3 +1441,4 @@ static struct platform_driver fsi_master_acf = {
module_platform_driver(fsi_master_acf);
MODULE_LICENSE("GPL");
MODULE_FIRMWARE(FW_FILE_NAME);

View File

@ -761,7 +761,7 @@ static DEVICE_ATTR(external_mode, 0664,
static void fsi_master_gpio_release(struct device *dev)
{
struct fsi_master_gpio *master = to_fsi_master_gpio(dev_to_fsi_master(dev));
struct fsi_master_gpio *master = to_fsi_master_gpio(to_fsi_master(dev));
of_node_put(dev_of_node(master->dev));

View File

@ -105,7 +105,7 @@ static int hub_master_link_enable(struct fsi_master *master, int link,
static void hub_master_release(struct device *dev)
{
struct fsi_master_hub *hub = to_fsi_master_hub(dev_to_fsi_master(dev));
struct fsi_master_hub *hub = to_fsi_master_hub(to_fsi_master(dev));
kfree(hub);
}

View File

@ -0,0 +1,316 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) IBM Corporation 2023 */
#include <linux/device.h>
#include <linux/fsi.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/mutex.h>
#include "fsi-master-i2cr.h"
#define CREATE_TRACE_POINTS
#include <trace/events/fsi_master_i2cr.h>
#define I2CR_ADDRESS_CFAM(a) ((a) >> 2)
#define I2CR_INITIAL_PARITY true
#define I2CR_STATUS_CMD 0x60002
#define I2CR_STATUS_ERR BIT_ULL(61)
#define I2CR_ERROR_CMD 0x60004
#define I2CR_LOG_CMD 0x60008
static const u8 i2cr_cfam[] = {
0xc0, 0x02, 0x0d, 0xa6,
0x80, 0x01, 0x10, 0x02,
0x80, 0x01, 0x10, 0x02,
0x80, 0x01, 0x10, 0x02,
0x80, 0x01, 0x80, 0x52,
0x80, 0x01, 0x10, 0x02,
0x80, 0x01, 0x10, 0x02,
0x80, 0x01, 0x10, 0x02,
0x80, 0x01, 0x10, 0x02,
0x80, 0x01, 0x22, 0x2d,
0x00, 0x00, 0x00, 0x00,
0xde, 0xad, 0xc0, 0xde
};
static bool i2cr_check_parity32(u32 v, bool parity)
{
u32 i;
for (i = 0; i < 32; ++i) {
if (v & (1u << i))
parity = !parity;
}
return parity;
}
static bool i2cr_check_parity64(u64 v)
{
u32 i;
bool parity = I2CR_INITIAL_PARITY;
for (i = 0; i < 64; ++i) {
if (v & (1llu << i))
parity = !parity;
}
return parity;
}
static u32 i2cr_get_command(u32 address, bool parity)
{
address <<= 1;
if (i2cr_check_parity32(address, parity))
address |= 1;
return address;
}
static int i2cr_transfer(struct i2c_client *client, u32 command, u64 *data)
{
struct i2c_msg msgs[2];
int ret;
msgs[0].addr = client->addr;
msgs[0].flags = 0;
msgs[0].len = sizeof(command);
msgs[0].buf = (__u8 *)&command;
msgs[1].addr = client->addr;
msgs[1].flags = I2C_M_RD;
msgs[1].len = sizeof(*data);
msgs[1].buf = (__u8 *)data;
ret = i2c_transfer(client->adapter, msgs, 2);
if (ret == 2)
return 0;
trace_i2cr_i2c_error(client, command, ret);
if (ret < 0)
return ret;
return -EIO;
}
static int i2cr_check_status(struct i2c_client *client)
{
u64 status;
int ret;
ret = i2cr_transfer(client, I2CR_STATUS_CMD, &status);
if (ret)
return ret;
if (status & I2CR_STATUS_ERR) {
u32 buf[3] = { 0, 0, 0 };
u64 error;
u64 log;
i2cr_transfer(client, I2CR_ERROR_CMD, &error);
i2cr_transfer(client, I2CR_LOG_CMD, &log);
trace_i2cr_status_error(client, status, error, log);
buf[0] = I2CR_STATUS_CMD;
i2c_master_send(client, (const char *)buf, sizeof(buf));
buf[0] = I2CR_ERROR_CMD;
i2c_master_send(client, (const char *)buf, sizeof(buf));
buf[0] = I2CR_LOG_CMD;
i2c_master_send(client, (const char *)buf, sizeof(buf));
dev_err(&client->dev, "status:%016llx error:%016llx log:%016llx\n", status, error,
log);
return -EREMOTEIO;
}
trace_i2cr_status(client, status);
return 0;
}
int fsi_master_i2cr_read(struct fsi_master_i2cr *i2cr, u32 addr, u64 *data)
{
u32 command = i2cr_get_command(addr, I2CR_INITIAL_PARITY);
int ret;
mutex_lock(&i2cr->lock);
ret = i2cr_transfer(i2cr->client, command, data);
if (ret)
goto unlock;
ret = i2cr_check_status(i2cr->client);
if (ret)
goto unlock;
trace_i2cr_read(i2cr->client, command, data);
unlock:
mutex_unlock(&i2cr->lock);
return ret;
}
EXPORT_SYMBOL_GPL(fsi_master_i2cr_read);
int fsi_master_i2cr_write(struct fsi_master_i2cr *i2cr, u32 addr, u64 data)
{
u32 buf[3] = { 0 };
int ret;
buf[0] = i2cr_get_command(addr, i2cr_check_parity64(data));
memcpy(&buf[1], &data, sizeof(data));
mutex_lock(&i2cr->lock);
ret = i2c_master_send(i2cr->client, (const char *)buf, sizeof(buf));
if (ret == sizeof(buf)) {
ret = i2cr_check_status(i2cr->client);
if (!ret)
trace_i2cr_write(i2cr->client, buf[0], data);
} else {
trace_i2cr_i2c_error(i2cr->client, buf[0], ret);
if (ret >= 0)
ret = -EIO;
}
mutex_unlock(&i2cr->lock);
return ret;
}
EXPORT_SYMBOL_GPL(fsi_master_i2cr_write);
static int i2cr_read(struct fsi_master *master, int link, uint8_t id, uint32_t addr, void *val,
size_t size)
{
struct fsi_master_i2cr *i2cr = container_of(master, struct fsi_master_i2cr, master);
u64 data;
size_t i;
int ret;
if (link || id || (addr & 0xffff0000) || !(size == 1 || size == 2 || size == 4))
return -EINVAL;
/*
* The I2CR doesn't have CFAM or FSI slave address space - only the
* engines. In order for this to work with the FSI core, we need to
* emulate at minimum the CFAM config table so that the appropriate
* engines are discovered.
*/
if (addr < 0xc00) {
if (addr > sizeof(i2cr_cfam) - 4)
addr = (addr & 0x3) + (sizeof(i2cr_cfam) - 4);
memcpy(val, &i2cr_cfam[addr], size);
return 0;
}
ret = fsi_master_i2cr_read(i2cr, I2CR_ADDRESS_CFAM(addr), &data);
if (ret)
return ret;
/*
* FSI core expects up to 4 bytes BE back, while I2CR replied with LE
* bytes on the wire.
*/
for (i = 0; i < size; ++i)
((u8 *)val)[i] = ((u8 *)&data)[7 - i];
return 0;
}
static int i2cr_write(struct fsi_master *master, int link, uint8_t id, uint32_t addr,
const void *val, size_t size)
{
struct fsi_master_i2cr *i2cr = container_of(master, struct fsi_master_i2cr, master);
u64 data = 0;
size_t i;
if (link || id || (addr & 0xffff0000) || !(size == 1 || size == 2 || size == 4))
return -EINVAL;
/* I2CR writes to CFAM or FSI slave address are a successful no-op. */
if (addr < 0xc00)
return 0;
/*
* FSI core passes up to 4 bytes BE, while the I2CR expects LE bytes on
* the wire.
*/
for (i = 0; i < size; ++i)
((u8 *)&data)[7 - i] = ((u8 *)val)[i];
return fsi_master_i2cr_write(i2cr, I2CR_ADDRESS_CFAM(addr), data);
}
static void i2cr_release(struct device *dev)
{
struct fsi_master_i2cr *i2cr = to_fsi_master_i2cr(to_fsi_master(dev));
of_node_put(dev->of_node);
kfree(i2cr);
}
static int i2cr_probe(struct i2c_client *client)
{
struct fsi_master_i2cr *i2cr;
int ret;
i2cr = kzalloc(sizeof(*i2cr), GFP_KERNEL);
if (!i2cr)
return -ENOMEM;
/* Only one I2CR on any given I2C bus (fixed I2C device address) */
i2cr->master.idx = client->adapter->nr;
dev_set_name(&i2cr->master.dev, "i2cr%d", i2cr->master.idx);
i2cr->master.dev.parent = &client->dev;
i2cr->master.dev.of_node = of_node_get(dev_of_node(&client->dev));
i2cr->master.dev.release = i2cr_release;
i2cr->master.n_links = 1;
i2cr->master.read = i2cr_read;
i2cr->master.write = i2cr_write;
mutex_init(&i2cr->lock);
i2cr->client = client;
ret = fsi_master_register(&i2cr->master);
if (ret)
return ret;
i2c_set_clientdata(client, i2cr);
return 0;
}
static void i2cr_remove(struct i2c_client *client)
{
struct fsi_master_i2cr *i2cr = i2c_get_clientdata(client);
fsi_master_unregister(&i2cr->master);
}
static const struct of_device_id i2cr_ids[] = {
{ .compatible = "ibm,i2cr-fsi-master" },
{ }
};
MODULE_DEVICE_TABLE(of, i2cr_ids);
static struct i2c_driver i2cr_driver = {
.probe = i2cr_probe,
.remove = i2cr_remove,
.driver = {
.name = "fsi-master-i2cr",
.of_match_table = i2cr_ids,
},
};
module_i2c_driver(i2cr_driver)
MODULE_AUTHOR("Eddie James <eajames@linux.ibm.com>");
MODULE_DESCRIPTION("IBM I2C Responder virtual FSI master driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,33 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (C) IBM Corporation 2023 */
#ifndef DRIVERS_FSI_MASTER_I2CR_H
#define DRIVERS_FSI_MASTER_I2CR_H
#include <linux/i2c.h>
#include <linux/mutex.h>
#include "fsi-master.h"
struct i2c_client;
struct fsi_master_i2cr {
struct fsi_master master;
struct mutex lock; /* protect HW access */
struct i2c_client *client;
};
#define to_fsi_master_i2cr(m) container_of(m, struct fsi_master_i2cr, master)
int fsi_master_i2cr_read(struct fsi_master_i2cr *i2cr, u32 addr, u64 *data);
int fsi_master_i2cr_write(struct fsi_master_i2cr *i2cr, u32 addr, u64 data);
static inline bool is_fsi_master_i2cr(struct fsi_master *master)
{
if (master->dev.parent && master->dev.parent->type == &i2c_client_type)
return true;
return false;
}
#endif /* DRIVERS_FSI_MASTER_I2CR_H */

View File

@ -136,7 +136,7 @@ struct fsi_master {
u8 t_send_delay, u8 t_echo_delay);
};
#define dev_to_fsi_master(d) container_of(d, struct fsi_master, dev)
#define to_fsi_master(d) container_of(d, struct fsi_master, dev)
/**
* fsi_master registration & lifetime: the fsi_master_register() and

View File

@ -15,7 +15,7 @@
#include <linux/mutex.h>
#include <linux/fsi-occ.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/slab.h>

View File

@ -22,8 +22,8 @@
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
@ -81,7 +81,7 @@
enum sbe_state
{
SBE_STATE_UNKNOWN = 0x0, // Unkown, initial state
SBE_STATE_UNKNOWN = 0x0, // Unknown, initial state
SBE_STATE_IPLING = 0x1, // IPL'ing - autonomous mode (transient)
SBE_STATE_ISTEP = 0x2, // ISTEP - Running IPL by steps (transient)
SBE_STATE_MPIPL = 0x3, // MPIPL
@ -127,6 +127,7 @@ struct sbefifo {
bool dead;
bool async_ffdc;
bool timed_out;
u32 timeout_in_cmd_ms;
u32 timeout_start_rsp_ms;
};
@ -136,6 +137,7 @@ struct sbefifo_user {
void *cmd_page;
void *pending_cmd;
size_t pending_len;
u32 cmd_timeout_ms;
u32 read_timeout_ms;
};
@ -508,7 +510,7 @@ static int sbefifo_send_command(struct sbefifo *sbefifo,
rc = sbefifo_wait(sbefifo, true, &status, timeout);
if (rc < 0)
return rc;
timeout = msecs_to_jiffies(SBEFIFO_TIMEOUT_IN_CMD);
timeout = msecs_to_jiffies(sbefifo->timeout_in_cmd_ms);
vacant = sbefifo_vacant(status);
len = chunk = min(vacant, remaining);
@ -730,7 +732,7 @@ static int __sbefifo_submit(struct sbefifo *sbefifo,
* @response: The output response buffer
* @resp_len: In: Response buffer size, Out: Response size
*
* This will perform the entire operation. If the reponse buffer
* This will perform the entire operation. If the response buffer
* overflows, returns -EOVERFLOW
*/
int sbefifo_submit(struct device *dev, const __be32 *command, size_t cmd_len,
@ -802,6 +804,7 @@ static int sbefifo_user_open(struct inode *inode, struct file *file)
return -ENOMEM;
}
mutex_init(&user->file_lock);
user->cmd_timeout_ms = SBEFIFO_TIMEOUT_IN_CMD;
user->read_timeout_ms = SBEFIFO_TIMEOUT_START_RSP;
return 0;
@ -845,9 +848,11 @@ static ssize_t sbefifo_user_read(struct file *file, char __user *buf,
rc = mutex_lock_interruptible(&sbefifo->lock);
if (rc)
goto bail;
sbefifo->timeout_in_cmd_ms = user->cmd_timeout_ms;
sbefifo->timeout_start_rsp_ms = user->read_timeout_ms;
rc = __sbefifo_submit(sbefifo, user->pending_cmd, cmd_len, &resp_iter);
sbefifo->timeout_start_rsp_ms = SBEFIFO_TIMEOUT_START_RSP;
sbefifo->timeout_in_cmd_ms = SBEFIFO_TIMEOUT_IN_CMD;
mutex_unlock(&sbefifo->lock);
if (rc < 0)
goto bail;
@ -937,6 +942,25 @@ static int sbefifo_user_release(struct inode *inode, struct file *file)
return 0;
}
static int sbefifo_cmd_timeout(struct sbefifo_user *user, void __user *argp)
{
struct device *dev = &user->sbefifo->dev;
u32 timeout;
if (get_user(timeout, (__u32 __user *)argp))
return -EFAULT;
if (timeout == 0) {
user->cmd_timeout_ms = SBEFIFO_TIMEOUT_IN_CMD;
dev_dbg(dev, "Command timeout reset to %us\n", user->cmd_timeout_ms / 1000);
return 0;
}
user->cmd_timeout_ms = timeout * 1000; /* user timeout is in sec */
dev_dbg(dev, "Command timeout set to %us\n", timeout);
return 0;
}
static int sbefifo_read_timeout(struct sbefifo_user *user, void __user *argp)
{
struct device *dev = &user->sbefifo->dev;
@ -947,17 +971,12 @@ static int sbefifo_read_timeout(struct sbefifo_user *user, void __user *argp)
if (timeout == 0) {
user->read_timeout_ms = SBEFIFO_TIMEOUT_START_RSP;
dev_dbg(dev, "Timeout reset to %d\n", user->read_timeout_ms);
dev_dbg(dev, "Timeout reset to %us\n", user->read_timeout_ms / 1000);
return 0;
}
if (timeout < 10 || timeout > 120)
return -EINVAL;
user->read_timeout_ms = timeout * 1000; /* user timeout is in sec */
dev_dbg(dev, "Timeout set to %d\n", user->read_timeout_ms);
dev_dbg(dev, "Timeout set to %us\n", timeout);
return 0;
}
@ -971,6 +990,9 @@ static long sbefifo_user_ioctl(struct file *file, unsigned int cmd, unsigned lon
mutex_lock(&user->file_lock);
switch (cmd) {
case FSI_SBEFIFO_CMD_TIMEOUT_SECONDS:
rc = sbefifo_cmd_timeout(user, (void __user *)arg);
break;
case FSI_SBEFIFO_READ_TIMEOUT_SECONDS:
rc = sbefifo_read_timeout(user, (void __user *)arg);
break;
@ -1025,16 +1047,9 @@ static int sbefifo_probe(struct device *dev)
sbefifo->fsi_dev = fsi_dev;
dev_set_drvdata(dev, sbefifo);
mutex_init(&sbefifo->lock);
sbefifo->timeout_in_cmd_ms = SBEFIFO_TIMEOUT_IN_CMD;
sbefifo->timeout_start_rsp_ms = SBEFIFO_TIMEOUT_START_RSP;
/*
* Try cleaning up the FIFO. If this fails, we still register the
* driver and will try cleaning things up again on the next access.
*/
rc = sbefifo_cleanup_hw(sbefifo);
if (rc && rc != -ESHUTDOWN)
dev_err(dev, "Initial HW cleanup failed, will retry later\n");
/* Create chardev for userspace access */
sbefifo->dev.type = &fsi_cdev_type;
sbefifo->dev.parent = dev;

View File

@ -10,6 +10,7 @@
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/mod_devicetable.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/list.h>
@ -587,6 +588,12 @@ static int scom_remove(struct device *dev)
return 0;
}
static const struct of_device_id scom_of_ids[] = {
{ .compatible = "ibm,fsi2pib" },
{ }
};
MODULE_DEVICE_TABLE(of, scom_of_ids);
static const struct fsi_device_id scom_ids[] = {
{
.engine_type = FSI_ENGID_SCOM,
@ -600,6 +607,7 @@ static struct fsi_driver scom_drv = {
.drv = {
.name = "scom",
.bus = &fsi_bus_type,
.of_match_table = scom_of_ids,
.probe = scom_probe,
.remove = scom_remove,
}

28
drivers/fsi/fsi-slave.h Normal file
View File

@ -0,0 +1,28 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (C) IBM Corporation 2023 */
#ifndef DRIVERS_FSI_SLAVE_H
#define DRIVERS_FSI_SLAVE_H
#include <linux/cdev.h>
#include <linux/device.h>
struct fsi_master;
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 to_fsi_slave(d) container_of(d, struct fsi_slave, dev)
#endif /* DRIVERS_FSI_SLAVE_H */

154
drivers/fsi/i2cr-scom.c Normal file
View File

@ -0,0 +1,154 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) IBM Corporation 2023 */
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/fsi.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include "fsi-master-i2cr.h"
#include "fsi-slave.h"
struct i2cr_scom {
struct device dev;
struct cdev cdev;
struct fsi_master_i2cr *i2cr;
};
static loff_t i2cr_scom_llseek(struct file *file, loff_t offset, int whence)
{
switch (whence) {
case SEEK_CUR:
break;
case SEEK_SET:
file->f_pos = offset;
break;
default:
return -EINVAL;
}
return offset;
}
static ssize_t i2cr_scom_read(struct file *filep, char __user *buf, size_t len, loff_t *offset)
{
struct i2cr_scom *scom = filep->private_data;
u64 data;
int ret;
if (len != sizeof(data))
return -EINVAL;
ret = fsi_master_i2cr_read(scom->i2cr, (u32)*offset, &data);
if (ret)
return ret;
ret = copy_to_user(buf, &data, len);
if (ret)
return ret;
return len;
}
static ssize_t i2cr_scom_write(struct file *filep, const char __user *buf, size_t len,
loff_t *offset)
{
struct i2cr_scom *scom = filep->private_data;
u64 data;
int ret;
if (len != sizeof(data))
return -EINVAL;
ret = copy_from_user(&data, buf, len);
if (ret)
return ret;
ret = fsi_master_i2cr_write(scom->i2cr, (u32)*offset, data);
if (ret)
return ret;
return len;
}
static const struct file_operations i2cr_scom_fops = {
.owner = THIS_MODULE,
.open = simple_open,
.llseek = i2cr_scom_llseek,
.read = i2cr_scom_read,
.write = i2cr_scom_write,
};
static int i2cr_scom_probe(struct device *dev)
{
struct fsi_device *fsi_dev = to_fsi_dev(dev);
struct i2cr_scom *scom;
int didx;
int ret;
if (!is_fsi_master_i2cr(fsi_dev->slave->master))
return -ENODEV;
scom = devm_kzalloc(dev, sizeof(*scom), GFP_KERNEL);
if (!scom)
return -ENOMEM;
scom->i2cr = to_fsi_master_i2cr(fsi_dev->slave->master);
dev_set_drvdata(dev, scom);
scom->dev.type = &fsi_cdev_type;
scom->dev.parent = dev;
device_initialize(&scom->dev);
ret = fsi_get_new_minor(fsi_dev, fsi_dev_scom, &scom->dev.devt, &didx);
if (ret)
return ret;
dev_set_name(&scom->dev, "scom%d", didx);
cdev_init(&scom->cdev, &i2cr_scom_fops);
ret = cdev_device_add(&scom->cdev, &scom->dev);
if (ret)
fsi_free_minor(scom->dev.devt);
return ret;
}
static int i2cr_scom_remove(struct device *dev)
{
struct i2cr_scom *scom = dev_get_drvdata(dev);
cdev_device_del(&scom->cdev, &scom->dev);
fsi_free_minor(scom->dev.devt);
return 0;
}
static const struct of_device_id i2cr_scom_of_ids[] = {
{ .compatible = "ibm,i2cr-scom" },
{ }
};
MODULE_DEVICE_TABLE(of, i2cr_scom_of_ids);
static const struct fsi_device_id i2cr_scom_ids[] = {
{ 0x5, FSI_VERSION_ANY },
{ }
};
static struct fsi_driver i2cr_scom_driver = {
.id_table = i2cr_scom_ids,
.drv = {
.name = "i2cr_scom",
.bus = &fsi_bus_type,
.of_match_table = i2cr_scom_of_ids,
.probe = i2cr_scom_probe,
.remove = i2cr_scom_remove,
}
};
module_fsi_driver(i2cr_scom_driver);
MODULE_AUTHOR("Eddie James <eajames@linux.ibm.com>");
MODULE_DESCRIPTION("IBM I2C Responder SCOM driver");
MODULE_LICENSE("GPL");

View File

@ -363,6 +363,7 @@ static int init_core_mask(struct peci_cputemp *priv)
switch (peci_dev->info.model) {
case INTEL_FAM6_ICELAKE_X:
case INTEL_FAM6_ICELAKE_D:
case INTEL_FAM6_SAPPHIRERAPIDS_X:
ret = peci_ep_pci_local_read(peci_dev, 0, reg->bus, reg->dev,
reg->func, reg->offset + 4, &data);
if (ret)
@ -531,6 +532,13 @@ static struct resolved_cores_reg resolved_cores_reg_icx = {
.offset = 0xd0,
};
static struct resolved_cores_reg resolved_cores_reg_spr = {
.bus = 31,
.dev = 30,
.func = 6,
.offset = 0x80,
};
static const struct cpu_info cpu_hsx = {
.reg = &resolved_cores_reg_hsx,
.min_peci_revision = 0x33,
@ -549,6 +557,12 @@ static const struct cpu_info cpu_icx = {
.thermal_margin_to_millidegree = &dts_ten_dot_six_to_millidegree,
};
static const struct cpu_info cpu_spr = {
.reg = &resolved_cores_reg_spr,
.min_peci_revision = 0x40,
.thermal_margin_to_millidegree = &dts_ten_dot_six_to_millidegree,
};
static const struct auxiliary_device_id peci_cputemp_ids[] = {
{
.name = "peci_cpu.cputemp.hsx",
@ -574,6 +588,10 @@ static const struct auxiliary_device_id peci_cputemp_ids[] = {
.name = "peci_cpu.cputemp.icxd",
.driver_data = (kernel_ulong_t)&cpu_icx,
},
{
.name = "peci_cpu.cputemp.spr",
.driver_data = (kernel_ulong_t)&cpu_spr,
},
{ }
};
MODULE_DEVICE_TABLE(auxiliary, peci_cputemp_ids);

View File

@ -30,6 +30,8 @@
#define DIMM_IDX_MAX_ON_ICX 2
#define CHAN_RANK_MAX_ON_ICXD 4
#define DIMM_IDX_MAX_ON_ICXD 2
#define CHAN_RANK_MAX_ON_SPR 8
#define DIMM_IDX_MAX_ON_SPR 2
#define CHAN_RANK_MAX CHAN_RANK_MAX_ON_HSX
#define DIMM_IDX_MAX DIMM_IDX_MAX_ON_HSX
@ -534,6 +536,43 @@ read_thresholds_icx(struct peci_dimmtemp *priv, int dimm_order, int chan_rank, u
return 0;
}
static int
read_thresholds_spr(struct peci_dimmtemp *priv, int dimm_order, int chan_rank, u32 *data)
{
u32 reg_val;
u64 offset;
int ret;
u8 dev;
ret = peci_ep_pci_local_read(priv->peci_dev, 0, 30, 0, 2, 0xd4, &reg_val);
if (ret || !(reg_val & BIT(31)))
return -ENODATA; /* Use default or previous value */
ret = peci_ep_pci_local_read(priv->peci_dev, 0, 30, 0, 2, 0xd0, &reg_val);
if (ret)
return -ENODATA; /* Use default or previous value */
/*
* Device 26, Offset 219a8: IMC 0 channel 0 -> rank 0
* Device 26, Offset 299a8: IMC 0 channel 1 -> rank 1
* Device 27, Offset 219a8: IMC 1 channel 0 -> rank 2
* Device 27, Offset 299a8: IMC 1 channel 1 -> rank 3
* Device 28, Offset 219a8: IMC 2 channel 0 -> rank 4
* Device 28, Offset 299a8: IMC 2 channel 1 -> rank 5
* Device 29, Offset 219a8: IMC 3 channel 0 -> rank 6
* Device 29, Offset 299a8: IMC 3 channel 1 -> rank 7
*/
dev = 26 + chan_rank / 2;
offset = 0x219a8 + dimm_order * 4 + (chan_rank % 2) * 0x8000;
ret = peci_mmio_read(priv->peci_dev, 0, GET_CPU_SEG(reg_val), GET_CPU_BUS(reg_val),
dev, 0, offset, data);
if (ret)
return ret;
return 0;
}
static const struct dimm_info dimm_hsx = {
.chan_rank_max = CHAN_RANK_MAX_ON_HSX,
.dimm_idx_max = DIMM_IDX_MAX_ON_HSX,
@ -576,6 +615,13 @@ static const struct dimm_info dimm_icxd = {
.read_thresholds = &read_thresholds_icx,
};
static const struct dimm_info dimm_spr = {
.chan_rank_max = CHAN_RANK_MAX_ON_SPR,
.dimm_idx_max = DIMM_IDX_MAX_ON_SPR,
.min_peci_revision = 0x40,
.read_thresholds = &read_thresholds_spr,
};
static const struct auxiliary_device_id peci_dimmtemp_ids[] = {
{
.name = "peci_cpu.dimmtemp.hsx",
@ -601,6 +647,10 @@ static const struct auxiliary_device_id peci_dimmtemp_ids[] = {
.name = "peci_cpu.dimmtemp.icxd",
.driver_data = (kernel_ulong_t)&dimm_icxd,
},
{
.name = "peci_cpu.dimmtemp.spr",
.driver_data = (kernel_ulong_t)&dimm_spr,
},
{ }
};
MODULE_DEVICE_TABLE(auxiliary, peci_dimmtemp_ids);

View File

@ -17,7 +17,7 @@
#include <linux/mutex.h>
#include <linux/clk.h>
#include <linux/coresight.h>
#include <linux/of_platform.h>
#include <linux/property.h>
#include <linux/delay.h>
#include <linux/pm_runtime.h>

View File

@ -22,7 +22,7 @@
#include "coresight-priv.h"
#include "coresight-cti.h"
/**
/*
* CTI devices can be associated with a PE, or be connected to CoreSight
* hardware. We have a list of all CTIs irrespective of CPU bound or
* otherwise.

View File

@ -147,17 +147,7 @@ static struct platform_driver dummy_driver = {
},
};
static int __init dummy_init(void)
{
return platform_driver_register(&dummy_driver);
}
module_init(dummy_init);
static void __exit dummy_exit(void)
{
platform_driver_unregister(&dummy_driver);
}
module_exit(dummy_exit);
module_platform_driver(dummy_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("CoreSight dummy driver");

View File

@ -40,7 +40,7 @@
* Invalid offsets will result in fail code return and feature load failure.
*
* @drvdata: driver data to map into.
* @reg: register to map.
* @reg_csdev: register to map.
* @offset: device offset for the register
*/
static int etm4_cfg_map_reg_offset(struct etmv4_drvdata *drvdata,
@ -132,7 +132,7 @@ static int etm4_cfg_map_reg_offset(struct etmv4_drvdata *drvdata,
* etm4_cfg_load_feature - load a feature into a device instance.
*
* @csdev: An ETMv4 CoreSight device.
* @feat: The feature to be loaded.
* @feat_csdev: The feature to be loaded.
*
* The function will load a feature instance into the device, checking that
* the register definitions are valid for the device.

View File

@ -3,6 +3,7 @@
* Copyright (c) 2014, The Linux Foundation. All rights reserved.
*/
#include <linux/acpi.h>
#include <linux/bitops.h>
#include <linux/kernel.h>
#include <linux/moduleparam.h>
@ -30,6 +31,7 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/property.h>
#include <linux/clk/clk-conf.h>
#include <asm/barrier.h>
#include <asm/sections.h>
@ -66,7 +68,6 @@ static u64 etm4_get_access_type(struct etmv4_config *config);
static enum cpuhp_state hp_online;
struct etm4_init_arg {
unsigned int pid;
struct device *dev;
struct csdev_access *csa;
};
@ -370,9 +371,17 @@ static void etm4_disable_arch_specific(struct etmv4_drvdata *drvdata)
}
static void etm4_check_arch_features(struct etmv4_drvdata *drvdata,
unsigned int id)
struct csdev_access *csa)
{
if (etm4_hisi_match_pid(id))
/*
* TRCPIDR* registers are not required for ETMs with system
* instructions. They must be identified by the MIDR+REVIDRs.
* Skip the TRCPID checks for now.
*/
if (!csa->io_mem)
return;
if (etm4_hisi_match_pid(coresight_get_pid(csa)))
set_bit(ETM4_IMPDEF_HISI_CORE_COMMIT, drvdata->arch_features);
}
#else
@ -385,7 +394,7 @@ static void etm4_disable_arch_specific(struct etmv4_drvdata *drvdata)
}
static void etm4_check_arch_features(struct etmv4_drvdata *drvdata,
unsigned int id)
struct csdev_access *csa)
{
}
#endif /* CONFIG_ETM4X_IMPDEF_FEATURE */
@ -1066,11 +1075,21 @@ static bool etm4_init_sysreg_access(struct etmv4_drvdata *drvdata,
return true;
}
static bool is_devtype_cpu_trace(void __iomem *base)
{
u32 devtype = readl(base + TRCDEVTYPE);
return (devtype == CS_DEVTYPE_PE_TRACE);
}
static bool etm4_init_iomem_access(struct etmv4_drvdata *drvdata,
struct csdev_access *csa)
{
u32 devarch = readl_relaxed(drvdata->base + TRCDEVARCH);
if (!is_coresight_device(drvdata->base) || !is_devtype_cpu_trace(drvdata->base))
return false;
/*
* All ETMs must implement TRCDEVARCH to indicate that
* the component is an ETMv4. Even though TRCIDR1 also
@ -1161,7 +1180,7 @@ static void etm4_init_arch_data(void *info)
etm4_os_unlock_csa(drvdata, csa);
etm4_cs_unlock(drvdata, csa);
etm4_check_arch_features(drvdata, init_arg->pid);
etm4_check_arch_features(drvdata, csa);
/* find all capabilities of the tracing unit */
etmidr0 = etm4x_relaxed_read32(csa, TRCIDR0);
@ -2044,19 +2063,16 @@ static int etm4_add_coresight_dev(struct etm4_init_arg *init_arg)
return 0;
}
static int etm4_probe(struct device *dev, void __iomem *base, u32 etm_pid)
static int etm4_probe(struct device *dev)
{
struct etmv4_drvdata *drvdata;
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev);
struct csdev_access access = { 0 };
struct etm4_init_arg init_arg = { 0 };
struct etm4_init_arg *delayed;
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata)
if (WARN_ON(!drvdata))
return -ENOMEM;
dev_set_drvdata(dev, drvdata);
if (pm_save_enable == PARAM_PM_SAVE_FIRMWARE)
pm_save_enable = coresight_loses_context_with_cpu(dev) ?
PARAM_PM_SAVE_SELF_HOSTED : PARAM_PM_SAVE_NEVER;
@ -2068,8 +2084,6 @@ static int etm4_probe(struct device *dev, void __iomem *base, u32 etm_pid)
return -ENOMEM;
}
drvdata->base = base;
spin_lock_init(&drvdata->spinlock);
drvdata->cpu = coresight_get_cpu(dev);
@ -2078,7 +2092,6 @@ static int etm4_probe(struct device *dev, void __iomem *base, u32 etm_pid)
init_arg.dev = dev;
init_arg.csa = &access;
init_arg.pid = etm_pid;
/*
* Serialize against CPUHP callbacks to avoid race condition
@ -2108,6 +2121,7 @@ static int etm4_probe(struct device *dev, void __iomem *base, u32 etm_pid)
static int etm4_probe_amba(struct amba_device *adev, const struct amba_id *id)
{
struct etmv4_drvdata *drvdata;
void __iomem *base;
struct device *dev = &adev->dev;
struct resource *res = &adev->res;
@ -2118,7 +2132,13 @@ static int etm4_probe_amba(struct amba_device *adev, const struct amba_id *id)
if (IS_ERR(base))
return PTR_ERR(base);
ret = etm4_probe(dev, base, id->id);
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata)
return -ENOMEM;
drvdata->base = base;
dev_set_drvdata(dev, drvdata);
ret = etm4_probe(dev);
if (!ret)
pm_runtime_put(&adev->dev);
@ -2127,18 +2147,32 @@ static int etm4_probe_amba(struct amba_device *adev, const struct amba_id *id)
static int etm4_probe_platform_dev(struct platform_device *pdev)
{
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
struct etmv4_drvdata *drvdata;
int ret;
drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata)
return -ENOMEM;
drvdata->pclk = coresight_get_enable_apb_pclk(&pdev->dev);
if (IS_ERR(drvdata->pclk))
return -ENODEV;
if (res) {
drvdata->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(drvdata->base)) {
clk_put(drvdata->pclk);
return PTR_ERR(drvdata->base);
}
}
dev_set_drvdata(&pdev->dev, drvdata);
pm_runtime_get_noresume(&pdev->dev);
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
/*
* System register based devices could match the
* HW by reading appropriate registers on the HW
* and thus we could skip the PID.
*/
ret = etm4_probe(&pdev->dev, NULL, 0);
ret = etm4_probe(&pdev->dev);
pm_runtime_put(&pdev->dev);
return ret;
@ -2178,7 +2212,7 @@ static struct amba_cs_uci_id uci_id_etm4[] = {
/* ETMv4 UCI data */
.devarch = ETM_DEVARCH_ETMv4x_ARCH,
.devarch_mask = ETM_DEVARCH_ID_MASK,
.devtype = 0x00000013,
.devtype = CS_DEVTYPE_PE_TRACE,
}
};
@ -2234,6 +2268,10 @@ static int __exit etm4_remove_platform_dev(struct platform_device *pdev)
if (drvdata)
etm4_remove_dev(drvdata);
pm_runtime_disable(&pdev->dev);
if (drvdata && !IS_ERR_OR_NULL(drvdata->pclk))
clk_put(drvdata->pclk);
return 0;
}
@ -2278,19 +2316,55 @@ static struct amba_driver etm4x_amba_driver = {
.id_table = etm4_ids,
};
#ifdef CONFIG_PM
static int etm4_runtime_suspend(struct device *dev)
{
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev);
if (drvdata->pclk && !IS_ERR(drvdata->pclk))
clk_disable_unprepare(drvdata->pclk);
return 0;
}
static int etm4_runtime_resume(struct device *dev)
{
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev);
if (drvdata->pclk && !IS_ERR(drvdata->pclk))
clk_prepare_enable(drvdata->pclk);
return 0;
}
#endif
static const struct dev_pm_ops etm4_dev_pm_ops = {
SET_RUNTIME_PM_OPS(etm4_runtime_suspend, etm4_runtime_resume, NULL)
};
static const struct of_device_id etm4_sysreg_match[] = {
{ .compatible = "arm,coresight-etm4x-sysreg" },
{ .compatible = "arm,embedded-trace-extension" },
{}
};
#ifdef CONFIG_ACPI
static const struct acpi_device_id etm4x_acpi_ids[] = {
{"ARMHC500", 0}, /* ARM CoreSight ETM4x */
{}
};
MODULE_DEVICE_TABLE(acpi, etm4x_acpi_ids);
#endif
static struct platform_driver etm4_platform_driver = {
.probe = etm4_probe_platform_dev,
.remove = etm4_remove_platform_dev,
.driver = {
.name = "coresight-etm4x",
.of_match_table = etm4_sysreg_match,
.acpi_match_table = ACPI_PTR(etm4x_acpi_ids),
.suppress_bind_attrs = true,
.pm = &etm4_dev_pm_ops,
},
};

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