Char/Misc drivers for 6.4-rc1

Here is the "big" set of char/misc and other driver subsystems for
 6.4-rc1.
 
 It's pretty big, but due to the removal of pcmcia drivers, almost breaks
 even for number of lines added vs. removed, a nice change.
 
 Included in here are:
   - removal of unused PCMCIA drivers (finally!)
   - Interconnect driver updates and additions
   - Lots of IIO driver updates and additions
   - MHI driver updates
   - Coresight driver updates
   - NVMEM driver updates, which required some OF updates
   - W1 driver updates and a new maintainer to manage the subsystem
   - FPGA driver updates
   - New driver subsystem, CDX, for AMD systems
   - lots of other small driver updates and additions
 
 All of these have been in linux-next for a while with no reported
 issues.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCZEp5Eg8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+ynSXgCg0kSw3vUYwpsnhAsQkoPw1QVA23sAn2edRCMa
 GEkPWjrROueCom7xbLMu
 =eR+P
 -----END PGP SIGNATURE-----

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

Pull char/misc drivers updates from Greg KH:
 "Here is the "big" set of char/misc and other driver subsystems for
  6.4-rc1.

  It's pretty big, but due to the removal of pcmcia drivers, almost
  breaks even for number of lines added vs. removed, a nice change.

  Included in here are:

   - removal of unused PCMCIA drivers (finally!)

   - Interconnect driver updates and additions

   - Lots of IIO driver updates and additions

   - MHI driver updates

   - Coresight driver updates

   - NVMEM driver updates, which required some OF updates

   - W1 driver updates and a new maintainer to manage the subsystem

   - FPGA driver updates

   - New driver subsystem, CDX, for AMD systems

   - lots of other small driver updates and additions

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

* tag 'char-misc-6.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (196 commits)
  mcb-lpc: Reallocate memory region to avoid memory overlapping
  mcb-pci: Reallocate memory region to avoid memory overlapping
  mcb: Return actual parsed size when reading chameleon table
  kernel/configs: Drop Android config fragments
  virt: acrn: Replace obsolete memalign() with posix_memalign()
  spmi: Add a check for remove callback when removing a SPMI driver
  spmi: fix W=1 kernel-doc warnings
  spmi: mtk-pmif: Drop of_match_ptr for ID table
  spmi: pmic-arb: Convert to platform remove callback returning void
  spmi: mtk-pmif: Convert to platform remove callback returning void
  spmi: hisi-spmi-controller: Convert to platform remove callback returning void
  w1: gpio: remove unnecessary ENOMEM messages
  w1: omap-hdq: remove unnecessary ENOMEM messages
  w1: omap-hdq: add SPDX tag
  w1: omap-hdq: allow compile testing
  w1: matrox: remove unnecessary ENOMEM messages
  w1: matrox: use inline over __inline__
  w1: matrox: switch from asm to linux header
  w1: ds2482: do not use assignment in if condition
  w1: ds2482: drop unnecessary header
  ...
This commit is contained in:
Linus Torvalds 2023-04-27 12:07:50 -07:00
commit cec24b8b6b
267 changed files with 11009 additions and 10977 deletions

View File

@ -3475,6 +3475,11 @@ D: several improvements to system programs
S: Oldenburg
S: Germany
N: Mathieu Poirier
E: mathieu.poirier@linaro.org
D: CoreSight kernel subsystem, Maintainer 2014-2022
D: Perf tool support for CoreSight
N: Robert Schwebel
E: robert@schwebel.de
W: https://www.schwebel.de

View File

@ -0,0 +1,56 @@
What: /sys/bus/cdx/rescan
Date: March 2023
Contact: nipun.gupta@amd.com
Description:
Writing y/1/on to this file will cause rescan of the bus
and devices on the CDX bus. Any new devices are scanned and
added to the list of Linux devices and any devices removed are
also deleted from Linux.
For example::
# echo 1 > /sys/bus/cdx/rescan
What: /sys/bus/cdx/devices/.../vendor
Date: March 2023
Contact: nipun.gupta@amd.com
Description:
Vendor ID for this CDX device, in hexadecimal. Vendor ID is
16 bit identifier which is specific to the device manufacturer.
Combination of Vendor ID and Device ID identifies a device.
What: /sys/bus/cdx/devices/.../device
Date: March 2023
Contact: nipun.gupta@amd.com
Description:
Device ID for this CDX device, in hexadecimal. Device ID is
16 bit identifier to identify a device type within the range
of a device manufacturer.
Combination of Vendor ID and Device ID identifies a device.
What: /sys/bus/cdx/devices/.../reset
Date: March 2023
Contact: nipun.gupta@amd.com
Description:
Writing y/1/on to this file resets the CDX device.
On resetting the device, the corresponding driver is notified
twice, once before the device is being reset, and again after
the reset has been complete.
For example::
# echo 1 > /sys/bus/cdx/.../reset
What: /sys/bus/cdx/devices/.../remove
Date: March 2023
Contact: tarak.reddy@amd.com
Description:
Writing y/1/on to this file removes the corresponding
device from the CDX bus. If the device is to be reconfigured
reconfigured in the Hardware, the device can be removed, so
that the device driver does not access the device while it is
being reconfigured.
For example::
# echo 1 > /sys/bus/cdx/devices/.../remove

View File

@ -1807,8 +1807,8 @@ What: /sys/bus/iio/devices/iio:deviceX/out_resistanceX_raw
KernelVersion: 4.3
Contact: linux-iio@vger.kernel.org
Description:
Raw (unscaled no offset etc.) resistance reading that can be processed
into an ohm value.
Raw (unscaled no offset etc.) resistance reading.
Units after application of scale and offset are ohms.
What: /sys/bus/iio/devices/iio:deviceX/heater_enable
KernelVersion: 4.1.0
@ -1894,8 +1894,9 @@ What: /sys/bus/iio/devices/iio:deviceX/in_electricalconductivity_raw
KernelVersion: 4.8
Contact: linux-iio@vger.kernel.org
Description:
Raw (unscaled no offset etc.) electric conductivity reading that
can be processed to siemens per meter.
Raw (unscaled no offset etc.) electric conductivity reading.
Units after application of scale and offset are siemens per
meter.
What: /sys/bus/iio/devices/iio:deviceX/in_countY_raw
KernelVersion: 4.10
@ -1951,8 +1952,8 @@ What: /sys/bus/iio/devices/iio:deviceX/in_phaseY_raw
KernelVersion: 4.18
Contact: linux-iio@vger.kernel.org
Description:
Raw (unscaled) phase difference reading from channel Y
that can be processed to radians.
Raw (unscaled) phase difference reading from channel Y.
Units after application of scale and offset are radians.
What: /sys/bus/iio/devices/iio:deviceX/in_massconcentration_pm1_input
What: /sys/bus/iio/devices/iio:deviceX/in_massconcentrationY_pm1_input

View File

@ -234,8 +234,8 @@ Description:
For details, see section `5.10 RAS Internal Error Register Definitions,
Altra Family Soc BMC Interface Specification`.
What: /sys/bus/platform/devices/smpro-errmon.*/event_[vrd_warn_fault|vrd_hot|dimm_hot]
KernelVersion: 6.1
What: /sys/bus/platform/devices/smpro-errmon.*/event_[vrd_warn_fault|vrd_hot|dimm_hot|dimm_2x_refresh]
KernelVersion: 6.1 (event_[vrd_warn_fault|vrd_hot|dimm_hot]), 6.4 (event_dimm_2x_refresh)
Contact: Quan Nguyen <quan@os.amperecomputing.com>
Description:
(RO) Contains the detail information in case of VRD/DIMM warning/hot events
@ -258,8 +258,21 @@ Description:
+---------------+---------------------------------------------------------------+---------------------+
| DIMM HOT | /sys/bus/platform/devices/smpro-errmon.*/event_dimm_hot | DIMM Hot |
+---------------+---------------------------------------------------------------+---------------------+
| DIMM 2X | /sys/bus/platform/devices/smpro-errmon.*/event_dimm_2x_refresh| DIMM 2x refresh rate|
| REFRESH RATE | | event in high temp |
+---------------+---------------------------------------------------------------+---------------------+
For more details, see section `5.7 GPI Status Registers,
For more details, see section `5.7 GPI Status Registers and 5.9 Memory Error Register Definitions,
Altra Family Soc BMC Interface Specification`.
What: /sys/bus/platform/devices/smpro-errmon.*/event_dimm[0-15]_syndrome
KernelVersion: 6.4
Contact: Quan Nguyen <quan@os.amperecomputing.com>
Description:
(RO) The sysfs returns the 2-byte DIMM failure syndrome data for slot
0-15 if it failed to initialize.
For more details, see section `5.11 Boot Stage Register Definitions,
Altra Family Soc BMC Interface Specification`.
What: /sys/bus/platform/devices/smpro-misc.*/boot_progress

View File

@ -0,0 +1,73 @@
What: /sys/bus/platform/drivers/zynqmp_fpga_manager/firmware:zynqmp-firmware:pcap/status
Date: February 2023
KernelVersion: 6.4
Contact: Nava kishore Manne <nava.kishore.manne@amd.com>
Description: (RO) Read fpga status.
Read returns a hexadecimal value that tells the current status
of the FPGA device. Each bit position in the status value is
described Below(see ug570 chapter 9).
https://docs.xilinx.com/v/u/en-US/ug570-ultrascale-configuration
====================== ==============================================
BIT(0) 0: No CRC error
1: CRC error
BIT(1) 0: Decryptor security not set
1: Decryptor security set
BIT(2) 0: MMCMs/PLLs are not locked
1: MMCMs/PLLs are locked
BIT(3) 0: DCI not matched
1: DCI matched
BIT(4) 0: Start-up sequence has not finished
1: Start-up sequence has finished
BIT(5) 0: All I/Os are placed in High-Z state
1: All I/Os behave as configured
BIT(6) 0: Flip-flops and block RAM are write disabled
1: Flip-flops and block RAM are write enabled
BIT(7) 0: GHIGH_B_STATUS asserted
1: GHIGH_B_STATUS deasserted
BIT(8) to BIT(10) Status of the mode pins
BIT(11) 0: Initialization has not finished
1: Initialization finished
BIT(12) Value on INIT_B_PIN pin
BIT(13) 0: Signal not released
1: Signal released
BIT(14) Value on DONE_PIN pin.
BIT(15) 0: No IDCODE_ERROR
1: IDCODE_ERROR
BIT(16) 0: No SECURITY_ERROR
1: SECURITY_ERROR
BIT(17) System Monitor over-temperature if set
BIT(18) to BIT(20) Start-up state machine (0 to 7)
Phase 0 = 000
Phase 1 = 001
Phase 2 = 011
Phase 3 = 010
Phase 4 = 110
Phase 5 = 111
Phase 6 = 101
Phase 7 = 100
BIT(25) to BIT(26) Indicates the detected bus width
00 = x1
01 = x8
10 = x16
11 = x32
====================== ==============================================
The other bits are reserved.

View File

@ -0,0 +1,82 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/bus/xlnx,versal-net-cdx.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: AMD CDX bus controller
description: |
CDX bus controller for AMD devices is implemented to dynamically
detect CDX bus and devices using the firmware.
The CDX bus manages multiple FPGA based hardware devices, which
can support network, crypto or any other specialized type of
devices. These FPGA based devices can be added/modified dynamically
on run-time.
All devices on the CDX bus will have a unique streamid (for IOMMU)
and a unique device ID (for MSI) corresponding to a requestor ID
(one to one associated with the device). The streamid and deviceid
are used to configure SMMU and GIC-ITS respectively.
iommu-map property is used to define the set of stream ids
corresponding to each device and the associated IOMMU.
The MSI writes are accompanied by sideband data (Device ID).
The msi-map property is used to associate the devices with the
device ID as well as the associated ITS controller.
rproc property (xlnx,rproc) is used to identify the remote processor
with which APU (Application Processor Unit) interacts to find out
the bus and device configuration.
maintainers:
- Nipun Gupta <nipun.gupta@amd.com>
- Nikhil Agarwal <nikhil.agarwal@amd.com>
properties:
compatible:
const: xlnx,versal-net-cdx
iommu-map: true
msi-map: true
xlnx,rproc:
$ref: /schemas/types.yaml#/definitions/phandle
description:
phandle to the remoteproc_r5 rproc node using which APU interacts
with remote processor.
ranges: true
"#address-cells":
enum: [1, 2]
"#size-cells":
enum: [1, 2]
required:
- compatible
- iommu-map
- msi-map
- xlnx,rproc
- ranges
- "#address-cells"
- "#size-cells"
additionalProperties: false
examples:
- |
cdx {
compatible = "xlnx,versal-net-cdx";
#address-cells = <1>;
#size-cells = <1>;
/* define map for RIDs 250-259 */
iommu-map = <250 &smmu 250 10>;
/* define msi map for RIDs 250-259 */
msi-map = <250 &its 250 10>;
xlnx,rproc = <&remoteproc_r5>;
ranges;
};

View File

@ -34,9 +34,11 @@ properties:
clock-names:
const: fck
power-domains: true
power-domains:
maxItems: 1
resets: true
resets:
maxItems: 1
"#address-cells":
const: 1
@ -51,6 +53,8 @@ required:
- reg
- clocks
- clock-names
- power-domains
- resets
- "#address-cells"
- "#size-cells"
@ -108,36 +112,30 @@ patternProperties:
examples:
- |
#include <dt-bindings/clock/r8a7791-clock.h>
#include <dt-bindings/clock/r8a7791-cpg-mssr.h>
#include <dt-bindings/power/r8a7791-sysc.h>
soc {
#address-cells = <2>;
#size-cells = <2>;
adc@e6e54000 {
compatible = "renesas,r8a7791-gyroadc", "renesas,rcar-gyroadc";
reg = <0 0xe6e54000 0 64>;
clocks = <&mstp9_clks R8A7791_CLK_GYROADC>;
clock-names = "fck";
power-domains = <&sysc R8A7791_PD_ALWAYS_ON>;
adc@e6e54000 {
compatible = "renesas,r8a7791-gyroadc", "renesas,rcar-gyroadc";
reg = <0xe6e54000 64>;
clocks = <&cpg CPG_MOD 901>;
clock-names = "fck";
power-domains = <&sysc R8A7791_PD_ALWAYS_ON>;
resets = <&cpg 901>;
pinctrl-0 = <&adc_pins>;
pinctrl-names = "default";
#address-cells = <1>;
#size-cells = <0>;
#address-cells = <1>;
#size-cells = <0>;
adc@0 {
reg = <0>;
compatible = "maxim,max1162";
vref-supply = <&vref_max1162>;
};
adc@0 {
reg = <0>;
compatible = "maxim,max1162";
vref-supply = <&vref_max1162>;
};
adc@1 {
reg = <1>;
compatible = "maxim,max1162";
vref-supply = <&vref_max1162>;
};
adc@1 {
reg = <1>;
compatible = "maxim,max1162";
vref-supply = <&vref_max1162>;
};
};
...

View File

@ -0,0 +1,46 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/adc/ti,ads1100.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: TI ADS1100/ADS1000 single channel I2C analog to digital converter
maintainers:
- Mike Looijmans <mike.looijmans@topic.nl>
description: |
Datasheet at: https://www.ti.com/lit/gpn/ads1100
properties:
compatible:
enum:
- ti,ads1100
- ti,ads1000
reg:
maxItems: 1
vdd-supply: true
"#io-channel-cells":
const: 0
required:
- compatible
- reg
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
adc@49 {
compatible = "ti,ads1100";
reg = <0x49>;
};
};
...

View File

@ -101,6 +101,15 @@ patternProperties:
When not configured as a comparator, the GPO will be treated as an
output-only GPIO.
drive-strength-microamp:
description: |
For channels configured as digital input, this configures the sink
current.
minimum: 0
maximum: 1800
default: 0
multipleOf: 120
required:
- reg

View File

@ -46,6 +46,9 @@ properties:
- items:
- const: st,ism330is
- const: st,lsm6dso16is
- items:
- const: st,asm330lhb
- const: st,asm330lhh
reg:
maxItems: 1

View File

@ -0,0 +1,46 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/light/rohm,bu27034.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: ROHM BU27034 ambient light sensor
maintainers:
- Matti Vaittinen <mazziesaccount@gmail.com>
description: |
ROHM BU27034 is an ambient light sesnor with 3 channels and 3 photo diodes
capable of detecting a very wide range of illuminance. Typical application
is adjusting LCD and backlight power of TVs and mobile phones.
https://fscdn.rohm.com/en/products/databook/datasheet/ic/sensor/light/bu27034nuc-e.pdf
properties:
compatible:
const: rohm,bu27034
reg:
maxItems: 1
vdd-supply: true
required:
- compatible
- reg
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
light-sensor@38 {
compatible = "rohm,bu27034";
reg = <0x38>;
vdd-supply = <&vdd>;
};
};
...

View File

@ -17,6 +17,7 @@ description: |
https://www.bosch-sensortec.com/bst/products/all_products/bmp280
https://www.bosch-sensortec.com/bst/products/all_products/bme280
https://www.bosch-sensortec.com/bst/products/all_products/bmp380
https://www.bosch-sensortec.com/bst/products/all_products/bmp580
properties:
compatible:
@ -26,6 +27,7 @@ properties:
- bosch,bmp280
- bosch,bme280
- bosch,bmp380
- bosch,bmp580
reg:
maxItems: 1

View File

@ -11,9 +11,6 @@ description: The STMicroelectronics sensor devices are pretty straight-forward
what type of sensor it is.
Note that whilst this covers many STMicro MEMs sensors, some more complex
IMUs need their own bindings.
The STMicroelectronics sensor devices are pretty straight-forward I2C or
SPI devices, all sharing the same device tree descriptions no matter what
type of sensor it is.
maintainers:
- Denis Ciocca <denis.ciocca@st.com>
@ -48,6 +45,9 @@ properties:
- st,lsm330d-accel
- st,lsm330dl-accel
- st,lsm330dlc-accel
- items:
- const: st,iis328dq
- const: st,h3lis331dl-accel
- description: Silan Accelerometers
enum:
- silan,sc7a20

View File

@ -18,6 +18,28 @@ description: |
https://www.analog.com/media/en/technical-documentation/data-sheets/29861fa.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/ltm2985.pdf
$defs:
sensor-node:
type: object
description: Sensor node common constraints
properties:
reg:
description:
Channel number. Connects the sensor to the channel with this number
of the device.
minimum: 1
maximum: 20
adi,sensor-type:
description: Type of sensor connected to the device.
$ref: /schemas/types.yaml#/definitions/uint32
required:
- reg
- adi,sensor-type
properties:
compatible:
oneOf:
@ -64,28 +86,10 @@ properties:
const: 0
patternProperties:
"@([0-9a-f]+)$":
type: object
description: Sensor.
properties:
reg:
description:
Channel number. Connects the sensor to the channel with this number
of the device.
minimum: 1
maximum: 20
adi,sensor-type:
description: Type of sensor connected to the device.
$ref: /schemas/types.yaml#/definitions/uint32
required:
- reg
- adi,sensor-type
"^thermocouple@":
type: object
$ref: '#/$defs/sensor-node'
unevaluatedProperties: false
description: Thermocouple sensor.
properties:
@ -123,7 +127,7 @@ patternProperties:
description:
Used for digitizing custom thermocouples.
See Page 59 of the datasheet.
$ref: /schemas/types.yaml#/definitions/uint64-matrix
$ref: /schemas/types.yaml#/definitions/int64-matrix
minItems: 3
maxItems: 64
items:
@ -141,7 +145,9 @@ patternProperties:
- adi,custom-thermocouple
"^diode@":
type: object
$ref: '#/$defs/sensor-node'
unevaluatedProperties: false
description: Diode sensor.
properties:
@ -184,7 +190,8 @@ patternProperties:
default: 0
"^rtd@":
type: object
$ref: '#/$defs/sensor-node'
unevaluatedProperties: false
description: RTD sensor.
properties:
@ -282,7 +289,8 @@ patternProperties:
- adi,custom-rtd
"^thermistor@":
type: object
$ref: '#/$defs/sensor-node'
unevaluatedProperties: false
description: Thermistor sensor.
properties:
@ -383,7 +391,8 @@ patternProperties:
- adi,custom-thermistor
"^adc@":
type: object
$ref: '#/$defs/sensor-node'
unevaluatedProperties: false
description: Direct ADC sensor.
properties:
@ -397,7 +406,8 @@ patternProperties:
type: boolean
"^temp@":
type: object
$ref: '#/$defs/sensor-node'
unevaluatedProperties: false
description: Active analog temperature sensor.
properties:
@ -426,7 +436,8 @@ patternProperties:
- adi,custom-temp
"^rsense@":
type: object
$ref: '#/$defs/sensor-node'
unevaluatedProperties: false
description: Sense resistor sensor.
properties:

View File

@ -7,9 +7,10 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: TI TMP117 - Digital temperature sensor with integrated NV memory
description: |
TI TMP117 - Digital temperature sensor with integrated NV memory that supports
I2C interface.
https://www.ti.com/lit/gpn/tmp1
TI TMP116/117 - Digital temperature sensor with integrated NV memory that
supports I2C interface.
https://www.ti.com/lit/gpn/tmp116
https://www.ti.com/lit/gpn/tmp117
maintainers:
- Puranjay Mohan <puranjay12@gmail.com>
@ -17,6 +18,7 @@ maintainers:
properties:
compatible:
enum:
- ti,tmp116
- ti,tmp117
reg:

View File

@ -22,14 +22,14 @@ description: |
properties:
compatible:
oneOf:
- const: qcom,msm8998-bwmon # BWMON v4
- items:
- enum:
- qcom,sc7280-cpu-bwmon
- qcom,sc8280xp-cpu-bwmon
- qcom,sdm845-bwmon
- qcom,sdm845-cpu-bwmon
- qcom,sm8550-cpu-bwmon
- const: qcom,msm8998-bwmon
- const: qcom,msm8998-bwmon # BWMON v4
- const: qcom,sdm845-bwmon # BWMON v4, unified register space
- items:
- enum:
- qcom,sc8280xp-llcc-bwmon
@ -49,9 +49,13 @@ properties:
type: object
reg:
# BWMON v4 (currently described) and BWMON v5 use one register address
# space. BWMON v2 uses two register spaces - not yet described.
maxItems: 1
# BWMON v5 uses one register address space, v1-v4 use one or two.
minItems: 1
maxItems: 2
reg-names:
minItems: 1
maxItems: 2
required:
- compatible
@ -63,13 +67,36 @@ required:
additionalProperties: false
allOf:
- if:
properties:
compatible:
const: qcom,msm8998-bwmon
then:
properties:
reg:
minItems: 2
reg-names:
items:
- const: monitor
- const: global
else:
properties:
reg:
maxItems: 1
reg-names:
maxItems: 1
examples:
- |
#include <dt-bindings/interconnect/qcom,sdm845.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
pmu@1436400 {
compatible = "qcom,sdm845-bwmon", "qcom,msm8998-bwmon";
compatible = "qcom,sdm845-cpu-bwmon", "qcom,sdm845-bwmon";
reg = <0x01436400 0x600>;
interrupts = <GIC_SPI 581 IRQ_TYPE_LEVEL_HIGH>;
interconnects = <&gladiator_noc MASTER_APPSS_PROC 3 &mem_noc SLAVE_LLCC 3>;

View File

@ -29,6 +29,7 @@ properties:
- enum:
- qcom,sc7280-epss-l3
- qcom,sc8280xp-epss-l3
- qcom,sm6375-cpucp-l3
- qcom,sm8250-epss-l3
- qcom,sm8350-epss-l3
- const: qcom,epss-l3

View File

@ -11,7 +11,7 @@ maintainers:
- Maxime Ripard <mripard@kernel.org>
allOf:
- $ref: "nvmem.yaml#"
- $ref: nvmem.yaml#
properties:
compatible:

View File

@ -0,0 +1,57 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/nvmem/amlogic,meson-gxbb-efuse.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Amlogic Meson GX eFuse
maintainers:
- Neil Armstrong <neil.armstrong@linaro.org>
allOf:
- $ref: nvmem.yaml#
properties:
compatible:
oneOf:
- const: amlogic,meson-gxbb-efuse
- items:
- const: amlogic,meson-gx-efuse
- const: amlogic,meson-gxbb-efuse
clocks:
maxItems: 1
secure-monitor:
description: phandle to the secure-monitor node
$ref: /schemas/types.yaml#/definitions/phandle
required:
- compatible
- clocks
- secure-monitor
unevaluatedProperties: false
examples:
- |
efuse: efuse {
compatible = "amlogic,meson-gxbb-efuse";
clocks = <&clk_efuse>;
#address-cells = <1>;
#size-cells = <1>;
secure-monitor = <&sm>;
sn: sn@14 {
reg = <0x14 0x10>;
};
eth_mac: mac@34 {
reg = <0x34 0x10>;
};
bid: bid@46 {
reg = <0x46 0x30>;
};
};

View File

@ -0,0 +1,57 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/nvmem/amlogic,meson6-efuse.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Amlogic Meson6 eFuse
maintainers:
- Neil Armstrong <neil.armstrong@linaro.org>
- Martin Blumenstingl <martin.blumenstingl@googlemail.com>
allOf:
- $ref: nvmem.yaml#
properties:
compatible:
enum:
- amlogic,meson6-efuse
- amlogic,meson8-efuse
- amlogic,meson8b-efuse
reg:
maxItems: 1
clocks:
maxItems: 1
clock-names:
const: core
required:
- compatible
- reg
- clocks
- clock-names
unevaluatedProperties: false
examples:
- |
efuse: efuse@0 {
compatible = "amlogic,meson6-efuse";
reg = <0x0 0x2000>;
clocks = <&clk_efuse>;
clock-names = "core";
#address-cells = <1>;
#size-cells = <1>;
ethernet_mac_address: mac@1b4 {
reg = <0x1b4 0x6>;
};
temperature_calib: calib@1f4 {
reg = <0x1f4 0x4>;
};
};

View File

@ -1,48 +0,0 @@
= Amlogic Meson GX eFuse device tree bindings =
Required properties:
- compatible: should be "amlogic,meson-gxbb-efuse"
- clocks: phandle to the efuse peripheral clock provided by the
clock controller.
- secure-monitor: phandle to the secure-monitor node
= Data cells =
Are child nodes of eFuse, bindings of which as described in
bindings/nvmem/nvmem.txt
Example:
efuse: efuse {
compatible = "amlogic,meson-gxbb-efuse";
clocks = <&clkc CLKID_EFUSE>;
#address-cells = <1>;
#size-cells = <1>;
secure-monitor = <&sm>;
sn: sn@14 {
reg = <0x14 0x10>;
};
eth_mac: eth_mac@34 {
reg = <0x34 0x10>;
};
bid: bid@46 {
reg = <0x46 0x30>;
};
};
sm: secure-monitor {
compatible = "amlogic,meson-gxbb-sm";
};
= Data consumers =
Are device nodes which consume nvmem data cells.
For example:
eth_mac {
...
nvmem-cells = <&eth_mac>;
nvmem-cell-names = "eth_mac";
};

View File

@ -1,22 +0,0 @@
Amlogic Meson6/Meson8/Meson8b efuse
Required Properties:
- compatible: depending on the SoC this should be one of:
- "amlogic,meson6-efuse"
- "amlogic,meson8-efuse"
- "amlogic,meson8b-efuse"
- reg: base address and size of the efuse registers
- clocks: a reference to the efuse core gate clock
- clock-names: must be "core"
All properties and sub-nodes as well as the consumer bindings
defined in nvmem.txt in this directory are also supported.
Example:
efuse: nvmem@0 {
compatible = "amlogic,meson8-efuse";
reg = <0x0 0x2000>;
clocks = <&clkc CLKID_EFUSE>;
clock-names = "core";
};

View File

@ -15,7 +15,7 @@ maintainers:
- Sven Peter <sven@svenpeter.dev>
allOf:
- $ref: "nvmem.yaml#"
- $ref: nvmem.yaml#
properties:
compatible:

View File

@ -20,7 +20,7 @@ maintainers:
- Rafał Miłecki <rafal@milecki.pl>
allOf:
- $ref: "nvmem.yaml#"
- $ref: nvmem.yaml#
properties:
compatible:

View File

@ -14,7 +14,7 @@ description: |
unique identifier per part.
allOf:
- $ref: "nvmem.yaml#"
- $ref: nvmem.yaml#
properties:
compatible:

View File

@ -14,7 +14,7 @@ description: |
i.MX25, i.MX27, i.MX31, i.MX35, i.MX51 and i.MX53 SoCs.
allOf:
- $ref: "nvmem.yaml#"
- $ref: nvmem.yaml#
properties:
compatible:

View File

@ -15,7 +15,7 @@ description: |
i.MX7D/S, i.MX7ULP, i.MX8MQ, i.MX8MM, i.MX8MN and i.MX8MP SoCs.
allOf:
- $ref: "nvmem.yaml#"
- $ref: nvmem.yaml#
properties:
compatible:

View File

@ -10,7 +10,7 @@ maintainers:
- PrasannaKumar Muralidharan <prasannatsmkumar@gmail.com>
allOf:
- $ref: "nvmem.yaml#"
- $ref: nvmem.yaml#
properties:
compatible:

View File

@ -61,7 +61,7 @@ properties:
type: object
additionalProperties: false
platforn-name:
platform-name:
type: object
additionalProperties: false

View File

@ -15,7 +15,7 @@ maintainers:
- Lala Lin <lala.lin@mediatek.com>
allOf:
- $ref: "nvmem.yaml#"
- $ref: nvmem.yaml#
properties:
$nodename:

View File

@ -15,7 +15,7 @@ description: |
settings, chip identifiers) or user specific data could be stored.
allOf:
- $ref: "nvmem.yaml#"
- $ref: nvmem.yaml#
properties:
compatible:

View File

@ -10,7 +10,7 @@ maintainers:
- Anson Huang <Anson.Huang@nxp.com>
allOf:
- $ref: "nvmem.yaml#"
- $ref: nvmem.yaml#
properties:
compatible:

View File

@ -17,7 +17,7 @@ maintainers:
- Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
allOf:
- $ref: "nvmem.yaml#"
- $ref: nvmem.yaml#
properties:
compatible:

View File

@ -10,7 +10,7 @@ maintainers:
- Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
allOf:
- $ref: "nvmem.yaml#"
- $ref: nvmem.yaml#
properties:
compatible:
@ -32,6 +32,8 @@ properties:
- qcom,sdm670-qfprom
- qcom,sdm845-qfprom
- qcom,sm6115-qfprom
- qcom,sm6350-qfprom
- qcom,sm6375-qfprom
- qcom,sm8150-qfprom
- qcom,sm8250-qfprom
- const: qcom,qfprom

View File

@ -15,7 +15,7 @@ description: |
to/from the PBUS.
allOf:
- $ref: "nvmem.yaml#"
- $ref: nvmem.yaml#
properties:
compatible:
@ -42,17 +42,22 @@ unevaluatedProperties: false
examples:
- |
sdam_1: nvram@b000 {
#address-cells = <1>;
#size-cells = <1>;
compatible = "qcom,spmi-sdam";
reg = <0xb000 0x100>;
ranges = <0 0xb000 0x100>;
pmic {
#address-cells = <1>;
#size-cells = <0>;
/* Data cells */
restart_reason: restart@50 {
reg = <0x50 0x1>;
bits = <6 2>;
};
};
sdam_1: nvram@b000 {
compatible = "qcom,spmi-sdam";
reg = <0xb000>;
#address-cells = <1>;
#size-cells = <1>;
ranges = <0 0xb000 0x100>;
/* Data cells */
restart_reason: restart@50 {
reg = <0x50 0x1>;
bits = <6 2>;
};
};
};
...

View File

@ -10,7 +10,7 @@ maintainers:
- Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
allOf:
- $ref: "nvmem.yaml#"
- $ref: nvmem.yaml#
properties:
compatible:

View File

@ -10,7 +10,7 @@ maintainers:
- Heiko Stuebner <heiko@sntech.de>
allOf:
- $ref: "nvmem.yaml#"
- $ref: nvmem.yaml#
properties:
compatible:

View File

@ -11,7 +11,7 @@ maintainers:
- Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
allOf:
- $ref: "nvmem.yaml#"
- $ref: nvmem.yaml#
properties:
"#address-cells": true

View File

@ -16,7 +16,7 @@ maintainers:
- Fabrice Gasnier <fabrice.gasnier@foss.st.com>
allOf:
- $ref: "nvmem.yaml#"
- $ref: nvmem.yaml#
properties:
compatible:

View File

@ -11,7 +11,7 @@ maintainers:
- Vincent Shih <vincent.sunplus@gmail.com>
allOf:
- $ref: "nvmem.yaml#"
- $ref: nvmem.yaml#
properties:
compatible:

View File

@ -50,7 +50,11 @@ properties:
ethaddr:
type: object
description: Ethernet interface's MAC address
description: Ethernet interfaces base MAC address.
properties:
"#nvmem-cell-cells":
description: The first argument is a MAC address offset.
const: 1
additionalProperties: false
@ -72,6 +76,7 @@ examples:
reg = <0x40000 0x10000>;
mac: ethaddr {
#nvmem-cell-cells = <1>;
};
};
};

View File

@ -0,0 +1,44 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/w1/maxim,ds2482.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Maxim One wire bus master controller
maintainers:
- Stefan Wahren <stefan.wahren@chargebyte.com>
description: |
I2C to 1-wire bridges
https://www.analog.com/media/en/technical-documentation/data-sheets/ds2482-100.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/DS2482-800.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/DS2484.pdf
properties:
compatible:
enum:
- maxim,ds2482
- maxim,ds2484
reg:
maxItems: 1
required:
- compatible
- reg
additionalProperties:
type: object
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
onewire@18 {
compatible = "maxim,ds2484";
reg = <0x18>;
};
};

View File

@ -185,3 +185,18 @@ ex::
=====================
See Documentation/devicetree/bindings/nvmem/nvmem.txt
8. NVMEM layouts
================
NVMEM layouts are yet another mechanism to create cells. With the device
tree binding it is possible to specify simple cells by using an offset
and a length. Sometimes, the cells doesn't have a static offset, but
the content is still well defined, e.g. tag-length-values. In this case,
the NVMEM device content has to be first parsed and the cells need to
be added accordingly. Layouts let you read the content of the NVMEM device
and let you add cells dynamically.
Another use case for layouts is the post processing of cells. With layouts,
it is possible to associate a custom post processing hook to a cell. It
even possible to add this hook to cells not created by the layout itself.

View File

@ -72,7 +72,6 @@ PG_MAGIC 'P' pg_{read,write}_hdr ``include/linux/
APM_BIOS_MAGIC 0x4101 apm_user ``arch/x86/kernel/apm_32.c``
FASYNC_MAGIC 0x4601 fasync_struct ``include/linux/fs.h``
SLIP_MAGIC 0x5302 slip ``drivers/net/slip.h``
MGSLPC_MAGIC 0x5402 mgslpc_info ``drivers/char/pcmcia/synclink_cs.c``
BAYCOM_MAGIC 0x19730510 baycom_state ``drivers/net/baycom_epp.c``
HDLCDRV_MAGIC 0x5ac6e778 hdlcdrv_state ``include/linux/hdlcdrv.h``
KV_MAGIC 0x5f4b565f kernel_vars_s ``arch/mips/include/asm/sn/klkernvars.h``

View File

@ -78,7 +78,6 @@ PG_MAGIC 'P' pg_{read,write}_hdr ``include/linux/
APM_BIOS_MAGIC 0x4101 apm_user ``arch/x86/kernel/apm_32.c``
FASYNC_MAGIC 0x4601 fasync_struct ``include/linux/fs.h``
SLIP_MAGIC 0x5302 slip ``drivers/net/slip.h``
MGSLPC_MAGIC 0x5402 mgslpc_info ``drivers/char/pcmcia/synclink_cs.c``
BAYCOM_MAGIC 0x19730510 baycom_state ``drivers/net/baycom_epp.c``
HDLCDRV_MAGIC 0x5ac6e778 hdlcdrv_state ``include/linux/hdlcdrv.h``
KV_MAGIC 0x5f4b565f kernel_vars_s ``arch/mips/include/asm/sn/klkernvars.h``

View File

@ -77,7 +77,6 @@ PG_MAGIC 'P' pg_{read,write}_hdr ``include/linux/
APM_BIOS_MAGIC 0x4101 apm_user ``arch/x86/kernel/apm_32.c``
FASYNC_MAGIC 0x4601 fasync_struct ``include/linux/fs.h``
SLIP_MAGIC 0x5302 slip ``drivers/net/slip.h``
MGSLPC_MAGIC 0x5402 mgslpc_info ``drivers/char/pcmcia/synclink_cs.c``
BAYCOM_MAGIC 0x19730510 baycom_state ``drivers/net/baycom_epp.c``
HDLCDRV_MAGIC 0x5ac6e778 hdlcdrv_state ``include/linux/hdlcdrv.h``
KV_MAGIC 0x5f4b565f kernel_vars_s ``arch/mips/include/asm/sn/klkernvars.h``

View File

@ -61,7 +61,6 @@ PG_MAGIC 'P' pg_{read,write}_hdr ``include/linux/
APM_BIOS_MAGIC 0x4101 apm_user ``arch/x86/kernel/apm_32.c``
FASYNC_MAGIC 0x4601 fasync_struct ``include/linux/fs.h``
SLIP_MAGIC 0x5302 slip ``drivers/net/slip.h``
MGSLPC_MAGIC 0x5402 mgslpc_info ``drivers/char/pcmcia/synclink_cs.c``
BAYCOM_MAGIC 0x19730510 baycom_state ``drivers/net/baycom_epp.c``
HDLCDRV_MAGIC 0x5ac6e778 hdlcdrv_state ``include/linux/hdlcdrv.h``
KV_MAGIC 0x5f4b565f kernel_vars_s ``arch/mips/include/asm/sn/klkernvars.h``

View File

@ -64,7 +64,6 @@ PG_MAGIC 'P' pg_{read,write}_hdr ``include/linux/
APM_BIOS_MAGIC 0x4101 apm_user ``arch/x86/kernel/apm_32.c``
FASYNC_MAGIC 0x4601 fasync_struct ``include/linux/fs.h``
SLIP_MAGIC 0x5302 slip ``drivers/net/slip.h``
MGSLPC_MAGIC 0x5402 mgslpc_info ``drivers/char/pcmcia/synclink_cs.c``
BAYCOM_MAGIC 0x19730510 baycom_state ``drivers/net/baycom_epp.c``
HDLCDRV_MAGIC 0x5ac6e778 hdlcdrv_state ``include/linux/hdlcdrv.h``
KV_MAGIC 0x5f4b565f kernel_vars_s ``arch/mips/include/asm/sn/klkernvars.h``

View File

@ -222,7 +222,6 @@ Code Seq# Include File Comments
'b' 00-FF conflict! bit3 vme host bridge
<mailto:natalia@nikhefk.nikhef.nl>
'b' 00-0F linux/dma-buf.h conflict!
'c' all linux/cm4000_cs.h conflict!
'c' 00-7F linux/comstats.h conflict!
'c' 00-7F linux/coda.h conflict!
'c' 00-1F linux/chio.h conflict!

View File

@ -964,6 +964,14 @@ Q: https://patchwork.kernel.org/project/linux-rdma/list/
F: drivers/infiniband/hw/efa/
F: include/uapi/rdma/efa-abi.h
AMD CDX BUS DRIVER
M: Nipun Gupta <nipun.gupta@amd.com>
M: Nikhil Agarwal <nikhil.agarwal@amd.com>
S: Maintained
F: Documentation/devicetree/bindings/bus/xlnx,versal-net-cdx.yaml
F: drivers/cdx/*
F: include/linux/cdx/*
AMD CRYPTOGRAPHIC COPROCESSOR (CCP) DRIVER
M: Tom Lendacky <thomas.lendacky@amd.com>
M: John Allen <john.allen@amd.com>
@ -1431,11 +1439,6 @@ S: Supported
F: drivers/clk/analogbits/*
F: include/linux/clk/analogbits*
ANDROID CONFIG FRAGMENTS
M: Rob Herring <robh@kernel.org>
S: Supported
F: kernel/configs/android*
ANDROID DRIVERS
M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
M: Arve Hjønnevåg <arve@android.com>
@ -2095,7 +2098,6 @@ F: arch/arm/boot/dts/cx92755*
N: digicolor
ARM/CORESIGHT FRAMEWORK AND DRIVERS
M: Mathieu Poirier <mathieu.poirier@linaro.org>
M: Suzuki K Poulose <suzuki.poulose@arm.com>
R: Mike Leach <mike.leach@linaro.org>
R: Leo Yan <leo.yan@linaro.org>
@ -10005,6 +10007,13 @@ F: Documentation/ABI/testing/sysfs-bus-iio-adc-envelope-detector
F: Documentation/devicetree/bindings/iio/adc/envelope-detector.yaml
F: drivers/iio/adc/envelope-detector.c
IIO LIGHT SENSOR GAIN-TIME-SCALE HELPERS
M: Matti Vaittinen <mazziesaccount@gmail.com>
L: linux-iio@vger.kernel.org
S: Maintained
F: drivers/iio/light/gain-time-scale-helper.c
F: drivers/iio/light/gain-time-scale-helper.h
IIO MULTIPLEXER
M: Peter Rosin <peda@axentia.se>
L: linux-iio@vger.kernel.org
@ -10528,6 +10537,7 @@ F: drivers/watchdog/mei_wdt.c
F: include/linux/mei_aux.h
F: include/linux/mei_cl_bus.h
F: include/uapi/linux/mei.h
F: include/uapi/linux/mei_uuid.h
F: include/uapi/linux/uuid.h
F: samples/mei/*
@ -15411,18 +15421,6 @@ S: Maintained
F: Documentation/filesystems/omfs.rst
F: fs/omfs/
OMNIKEY CARDMAN 4000 DRIVER
M: Harald Welte <laforge@gnumonks.org>
S: Maintained
F: drivers/char/pcmcia/cm4000_cs.c
F: include/linux/cm4000_cs.h
F: include/uapi/linux/cm4000_cs.h
OMNIKEY CARDMAN 4040 DRIVER
M: Harald Welte <laforge@gnumonks.org>
S: Maintained
F: drivers/char/pcmcia/cm4040_cs.*
OMNIVISION OG01A1B SENSOR DRIVER
M: Shawn Tu <shawnx.tu@intel.com>
L: linux-media@vger.kernel.org
@ -15640,6 +15638,12 @@ L: linux-hwmon@vger.kernel.org
S: Maintained
F: drivers/hwmon/oxp-sensors.c
ONIE TLV NVMEM LAYOUT DRIVER
M: Miquel Raynal <miquel.raynal@bootlin.com>
S: Maintained
F: Documentation/devicetree/bindings/nvmem/layouts/onie,tlv-layout.yaml
F: drivers/nvmem/layouts/onie-tlv.c
ONION OMEGA2+ BOARD
M: Harvey Hunt <harveyhuntnexus@gmail.com>
L: linux-mips@vger.kernel.org
@ -18203,6 +18207,12 @@ S: Maintained
F: Documentation/devicetree/bindings/iio/light/bh1750.yaml
F: drivers/iio/light/bh1750.c
ROHM BU27034 AMBIENT LIGHT SENSOR DRIVER
M: Matti Vaittinen <mazziesaccount@gmail.com>
L: linux-iio@vger.kernel.org
S: Supported
F: drivers/iio/light/rohm-bu27034.c
ROHM MULTIFUNCTION BD9571MWV-M PMIC DEVICE DRIVERS
M: Marek Vasut <marek.vasut+renesas@gmail.com>
L: linux-kernel@vger.kernel.org
@ -18727,11 +18737,6 @@ F: include/linux/wait.h
F: include/uapi/linux/sched.h
F: kernel/sched/
SCR24X CHIP CARD INTERFACE DRIVER
M: Lubomir Rintel <lkundrak@v3.sk>
S: Supported
F: drivers/char/pcmcia/scr24x_cs.c
SCSI RDMA PROTOCOL (SRP) INITIATOR
M: Bart Van Assche <bvanassche@acm.org>
L: linux-rdma@vger.kernel.org
@ -19310,6 +19315,12 @@ F: drivers/irqchip/irq-sl28cpld.c
F: drivers/pwm/pwm-sl28cpld.c
F: drivers/watchdog/sl28cpld_wdt.c
SL28 VPD NVMEM LAYOUT DRIVER
M: Michael Walle <michael@walle.cc>
S: Maintained
F: Documentation/devicetree/bindings/nvmem/layouts/kontron,sl28-vpd.yaml
F: drivers/nvmem/layouts/sl28vpd.c
SLAB ALLOCATOR
M: Christoph Lameter <cl@linux.com>
M: Pekka Enberg <penberg@kernel.org>
@ -22566,7 +22577,7 @@ S: Orphan
F: drivers/mmc/host/vub300.c
W1 DALLAS'S 1-WIRE BUS
M: Evgeniy Polyakov <zbr@ioremap.net>
M: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
S: Maintained
F: Documentation/devicetree/bindings/w1/
F: Documentation/w1/

View File

@ -614,8 +614,6 @@ CONFIG_HW_RANDOM=y
CONFIG_HW_RANDOM_VIRTIO=m
CONFIG_NVRAM=y
CONFIG_DTLK=m
CONFIG_CARDMAN_4000=m
CONFIG_CARDMAN_4040=m
CONFIG_IPWIRELESS=m
CONFIG_I2C_CHARDEV=m
CONFIG_I2C_HYDRA=m

View File

@ -241,4 +241,6 @@ source "drivers/peci/Kconfig"
source "drivers/hte/Kconfig"
source "drivers/cdx/Kconfig"
endmenu

View File

@ -194,3 +194,4 @@ obj-$(CONFIG_MOST) += most/
obj-$(CONFIG_PECI) += peci/
obj-$(CONFIG_HTE) += hte/
obj-$(CONFIG_DRM_ACCEL) += accel/
obj-$(CONFIG_CDX_BUS) += cdx/

View File

@ -810,9 +810,10 @@ static bool acpi_of_modalias(struct acpi_device *adev,
* @modalias: Pointer to buffer that modalias value will be copied into
* @len: Length of modalias buffer
*
* This is a counterpart of of_modalias_node() for struct acpi_device objects.
* If there is a compatible string for @adev, it will be copied to @modalias
* with the vendor prefix stripped; otherwise, @default_id will be used.
* This is a counterpart of of_alias_from_compatible() for struct acpi_device
* objects. If there is a compatible string for @adev, it will be copied to
* @modalias with the vendor prefix stripped; otherwise, @default_id will be
* used.
*/
void acpi_set_modalias(struct acpi_device *adev, const char *default_id,
char *modalias, size_t len)

View File

@ -126,7 +126,7 @@ static int mhi_ep_process_cmd_ring(struct mhi_ep_ring *ring, struct mhi_ring_ele
/* Check if the channel is supported by the controller */
if ((ch_id >= mhi_cntrl->max_chan) || !mhi_cntrl->mhi_chan[ch_id].name) {
dev_err(dev, "Channel (%u) not supported!\n", ch_id);
dev_dbg(dev, "Channel (%u) not supported!\n", ch_id);
return -ENODEV;
}
@ -702,7 +702,7 @@ static void mhi_ep_cmd_ring_worker(struct work_struct *work)
el = &ring->ring_cache[ring->rd_offset];
ret = mhi_ep_process_cmd_ring(ring, el);
if (ret)
if (ret && ret != -ENODEV)
dev_err(dev, "Error processing cmd ring element: %zu\n", ring->rd_offset);
mhi_ep_ring_inc_index(ring);

View File

@ -391,6 +391,7 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl)
{
const struct firmware *firmware = NULL;
struct device *dev = &mhi_cntrl->mhi_dev->dev;
enum mhi_pm_state new_state;
const char *fw_name;
void *buf;
dma_addr_t dma_addr;
@ -508,14 +509,18 @@ error_ready_state:
}
error_fw_load:
mhi_cntrl->pm_state = MHI_PM_FW_DL_ERR;
wake_up_all(&mhi_cntrl->state_event);
write_lock_irq(&mhi_cntrl->pm_lock);
new_state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_FW_DL_ERR);
write_unlock_irq(&mhi_cntrl->pm_lock);
if (new_state == MHI_PM_FW_DL_ERR)
wake_up_all(&mhi_cntrl->state_event);
}
int mhi_download_amss_image(struct mhi_controller *mhi_cntrl)
{
struct image_info *image_info = mhi_cntrl->fbc_image;
struct device *dev = &mhi_cntrl->mhi_dev->dev;
enum mhi_pm_state new_state;
int ret;
if (!image_info)
@ -526,8 +531,11 @@ int mhi_download_amss_image(struct mhi_controller *mhi_cntrl)
&image_info->mhi_buf[image_info->entries - 1]);
if (ret) {
dev_err(dev, "MHI did not load AMSS, ret:%d\n", ret);
mhi_cntrl->pm_state = MHI_PM_FW_DL_ERR;
wake_up_all(&mhi_cntrl->state_event);
write_lock_irq(&mhi_cntrl->pm_lock);
new_state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_FW_DL_ERR);
write_unlock_irq(&mhi_cntrl->pm_lock);
if (new_state == MHI_PM_FW_DL_ERR)
wake_up_all(&mhi_cntrl->state_event);
}
return ret;

View File

@ -516,6 +516,12 @@ int mhi_init_mmio(struct mhi_controller *mhi_cntrl)
return -EIO;
}
if (val >= mhi_cntrl->reg_len - (8 * MHI_DEV_WAKE_DB)) {
dev_err(dev, "CHDB offset: 0x%x is out of range: 0x%zx\n",
val, mhi_cntrl->reg_len - (8 * MHI_DEV_WAKE_DB));
return -ERANGE;
}
/* Setup wake db */
mhi_cntrl->wake_db = base + val + (8 * MHI_DEV_WAKE_DB);
mhi_cntrl->wake_set = false;
@ -532,6 +538,12 @@ int mhi_init_mmio(struct mhi_controller *mhi_cntrl)
return -EIO;
}
if (val >= mhi_cntrl->reg_len - (8 * mhi_cntrl->total_ev_rings)) {
dev_err(dev, "ERDB offset: 0x%x is out of range: 0x%zx\n",
val, mhi_cntrl->reg_len - (8 * mhi_cntrl->total_ev_rings));
return -ERANGE;
}
/* Setup event db address for each ev_ring */
mhi_event = mhi_cntrl->mhi_event;
for (i = 0; i < mhi_cntrl->total_ev_rings; i++, val += 8, mhi_event++) {
@ -1100,7 +1112,7 @@ int mhi_prepare_for_power_up(struct mhi_controller *mhi_cntrl)
if (bhi_off >= mhi_cntrl->reg_len) {
dev_err(dev, "BHI offset: 0x%x is out of range: 0x%zx\n",
bhi_off, mhi_cntrl->reg_len);
ret = -EINVAL;
ret = -ERANGE;
goto error_reg_offset;
}
mhi_cntrl->bhi = mhi_cntrl->regs + bhi_off;
@ -1117,7 +1129,7 @@ int mhi_prepare_for_power_up(struct mhi_controller *mhi_cntrl)
dev_err(dev,
"BHIe offset: 0x%x is out of range: 0x%zx\n",
bhie_off, mhi_cntrl->reg_len);
ret = -EINVAL;
ret = -ERANGE;
goto error_reg_offset;
}
mhi_cntrl->bhie = mhi_cntrl->regs + bhie_off;

View File

@ -503,7 +503,7 @@ irqreturn_t mhi_intvec_threaded_handler(int irq_number, void *priv)
}
write_unlock_irq(&mhi_cntrl->pm_lock);
if (pm_state != MHI_PM_SYS_ERR_DETECT || ee == mhi_cntrl->ee)
if (pm_state != MHI_PM_SYS_ERR_DETECT)
goto exit_intvec;
switch (ee) {
@ -961,7 +961,9 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl,
}
read_lock_bh(&mhi_cntrl->pm_lock);
if (likely(MHI_DB_ACCESS_VALID(mhi_cntrl)))
/* Ring EV DB only if there is any pending element to process */
if (likely(MHI_DB_ACCESS_VALID(mhi_cntrl)) && count)
mhi_ring_er_db(mhi_event);
read_unlock_bh(&mhi_cntrl->pm_lock);
@ -1031,7 +1033,9 @@ int mhi_process_data_event_ring(struct mhi_controller *mhi_cntrl,
count++;
}
read_lock_bh(&mhi_cntrl->pm_lock);
if (likely(MHI_DB_ACCESS_VALID(mhi_cntrl)))
/* Ring EV DB only if there is any pending element to process */
if (likely(MHI_DB_ACCESS_VALID(mhi_cntrl)) && count)
mhi_ring_er_db(mhi_event);
read_unlock_bh(&mhi_cntrl->pm_lock);
@ -1679,18 +1683,3 @@ void mhi_unprepare_from_transfer(struct mhi_device *mhi_dev)
}
}
EXPORT_SYMBOL_GPL(mhi_unprepare_from_transfer);
int mhi_poll(struct mhi_device *mhi_dev, u32 budget)
{
struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
struct mhi_chan *mhi_chan = mhi_dev->dl_chan;
struct mhi_event *mhi_event = &mhi_cntrl->mhi_event[mhi_chan->er_index];
int ret;
spin_lock_bh(&mhi_event->lock);
ret = mhi_event->process_event(mhi_cntrl, mhi_event, budget);
spin_unlock_bh(&mhi_event->lock);
return ret;
}
EXPORT_SYMBOL_GPL(mhi_poll);

View File

@ -8,7 +8,6 @@
* Copyright (C) 2020 Linaro Ltd <loic.poulain@linaro.org>
*/
#include <linux/aer.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/mhi.h>
@ -344,8 +343,6 @@ static const struct mhi_channel_config mhi_foxconn_sdx55_channels[] = {
MHI_CHANNEL_CONFIG_DL(13, "MBIM", 32, 0),
MHI_CHANNEL_CONFIG_UL(32, "DUN", 32, 0),
MHI_CHANNEL_CONFIG_DL(33, "DUN", 32, 0),
MHI_CHANNEL_CONFIG_UL(92, "DUN2", 32, 1),
MHI_CHANNEL_CONFIG_DL(93, "DUN2", 32, 1),
MHI_CHANNEL_CONFIG_HW_UL(100, "IP_HW0_MBIM", 128, 2),
MHI_CHANNEL_CONFIG_HW_DL(101, "IP_HW0_MBIM", 128, 3),
};
@ -366,6 +363,15 @@ static const struct mhi_controller_config modem_foxconn_sdx55_config = {
.event_cfg = mhi_foxconn_sdx55_events,
};
static const struct mhi_pci_dev_info mhi_foxconn_sdx24_info = {
.name = "foxconn-sdx24",
.config = &modem_foxconn_sdx55_config,
.bar_num = MHI_PCI_DEFAULT_BAR_NUM,
.dma_data_width = 32,
.mru_default = 32768,
.sideband_wake = false,
};
static const struct mhi_pci_dev_info mhi_foxconn_sdx55_info = {
.name = "foxconn-sdx55",
.fw = "qcom/sdx55m/sbl1.mbn",
@ -590,6 +596,15 @@ static const struct pci_device_id mhi_pci_id_table[] = {
/* T99W373 (sdx62) */
{ PCI_DEVICE(PCI_VENDOR_ID_FOXCONN, 0xe0d9),
.driver_data = (kernel_ulong_t) &mhi_foxconn_sdx65_info },
/* T99W510 (sdx24), variant 1 */
{ PCI_DEVICE(PCI_VENDOR_ID_FOXCONN, 0xe0f0),
.driver_data = (kernel_ulong_t) &mhi_foxconn_sdx24_info },
/* T99W510 (sdx24), variant 2 */
{ PCI_DEVICE(PCI_VENDOR_ID_FOXCONN, 0xe0f1),
.driver_data = (kernel_ulong_t) &mhi_foxconn_sdx24_info },
/* T99W510 (sdx24), variant 3 */
{ PCI_DEVICE(PCI_VENDOR_ID_FOXCONN, 0xe0f2),
.driver_data = (kernel_ulong_t) &mhi_foxconn_sdx24_info },
/* MV31-W (Cinterion) */
{ PCI_DEVICE(PCI_VENDOR_ID_THALES, 0x00b3),
.driver_data = (kernel_ulong_t) &mhi_mv31_info },
@ -903,11 +918,9 @@ static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
mhi_pdev->pci_state = pci_store_saved_state(pdev);
pci_load_saved_state(pdev, NULL);
pci_enable_pcie_error_reporting(pdev);
err = mhi_register_controller(mhi_cntrl, mhi_cntrl_config);
if (err)
goto err_disable_reporting;
return err;
/* MHI bus does not power up the controller by default */
err = mhi_prepare_for_power_up(mhi_cntrl);
@ -941,8 +954,6 @@ err_unprepare:
mhi_unprepare_after_power_down(mhi_cntrl);
err_unregister:
mhi_unregister_controller(mhi_cntrl);
err_disable_reporting:
pci_disable_pcie_error_reporting(pdev);
return err;
}
@ -965,7 +976,6 @@ static void mhi_pci_remove(struct pci_dev *pdev)
pm_runtime_get_noresume(&pdev->dev);
mhi_unregister_controller(mhi_cntrl);
pci_disable_pcie_error_reporting(pdev);
}
static void mhi_pci_shutdown(struct pci_dev *pdev)

19
drivers/cdx/Kconfig Normal file
View File

@ -0,0 +1,19 @@
# SPDX-License-Identifier: GPL-2.0
#
# CDX bus configuration
#
# Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
#
config CDX_BUS
bool "CDX Bus driver"
depends on OF && ARM64
help
Driver to enable Composable DMA Transfer(CDX) Bus. CDX bus
exposes Fabric devices which uses composable DMA IP to the
APU. CDX bus provides a mechanism for scanning and probing
of CDX devices. CDX devices are memory mapped on system bus
for embedded CPUs. CDX bus uses CDX controller and firmware
to scan these CDX devices.
source "drivers/cdx/controller/Kconfig"

8
drivers/cdx/Makefile Normal file
View File

@ -0,0 +1,8 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for CDX
#
# Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
#
obj-$(CONFIG_CDX_BUS) += cdx.o controller/

535
drivers/cdx/cdx.c Normal file
View File

@ -0,0 +1,535 @@
// SPDX-License-Identifier: GPL-2.0
/*
* CDX bus driver.
*
* Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
*/
/*
* Architecture Overview
* =====================
* CDX is a Hardware Architecture designed for AMD FPGA devices. It
* consists of sophisticated mechanism for interaction between FPGA,
* Firmware and the APUs (Application CPUs).
*
* Firmware resides on RPU (Realtime CPUs) which interacts with
* the FPGA program manager and the APUs. The RPU provides memory-mapped
* interface (RPU if) which is used to communicate with APUs.
*
* The diagram below shows an overview of the CDX architecture:
*
* +--------------------------------------+
* | Application CPUs (APU) |
* | |
* | CDX device drivers|
* | Linux OS | |
* | CDX bus |
* | | |
* | CDX controller |
* | | |
* +-----------------------------|--------+
* | (discover, config,
* | reset, rescan)
* |
* +------------------------| RPU if |----+
* | | |
* | V |
* | Realtime CPUs (RPU) |
* | |
* +--------------------------------------+
* |
* +---------------------|----------------+
* | FPGA | |
* | +-----------------------+ |
* | | | | |
* | +-------+ +-------+ +-------+ |
* | | dev 1 | | dev 2 | | dev 3 | |
* | +-------+ +-------+ +-------+ |
* +--------------------------------------+
*
* The RPU firmware extracts the device information from the loaded FPGA
* image and implements a mechanism that allows the APU drivers to
* enumerate such devices (device personality and resource details) via
* a dedicated communication channel. RPU mediates operations such as
* discover, reset and rescan of the FPGA devices for the APU. This is
* done using memory mapped interface provided by the RPU to APU.
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/of_device.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/xarray.h>
#include <linux/cdx/cdx_bus.h>
#include "cdx.h"
/* Default DMA mask for devices on a CDX bus */
#define CDX_DEFAULT_DMA_MASK (~0ULL)
#define MAX_CDX_CONTROLLERS 16
/* CDX controllers registered with the CDX bus */
static DEFINE_XARRAY_ALLOC(cdx_controllers);
/**
* cdx_dev_reset - Reset a CDX device
* @dev: CDX device
*
* Return: -errno on failure, 0 on success.
*/
int cdx_dev_reset(struct device *dev)
{
struct cdx_device *cdx_dev = to_cdx_device(dev);
struct cdx_controller *cdx = cdx_dev->cdx;
struct cdx_device_config dev_config = {0};
struct cdx_driver *cdx_drv;
int ret;
cdx_drv = to_cdx_driver(dev->driver);
/* Notify driver that device is being reset */
if (cdx_drv && cdx_drv->reset_prepare)
cdx_drv->reset_prepare(cdx_dev);
dev_config.type = CDX_DEV_RESET_CONF;
ret = cdx->ops->dev_configure(cdx, cdx_dev->bus_num,
cdx_dev->dev_num, &dev_config);
if (ret)
dev_err(dev, "cdx device reset failed\n");
/* Notify driver that device reset is complete */
if (cdx_drv && cdx_drv->reset_done)
cdx_drv->reset_done(cdx_dev);
return ret;
}
EXPORT_SYMBOL_GPL(cdx_dev_reset);
/**
* cdx_unregister_device - Unregister a CDX device
* @dev: CDX device
* @data: This is always passed as NULL, and is not used in this API,
* but is required here as the bus_for_each_dev() API expects
* the passed function (cdx_unregister_device) to have this
* as an argument.
*
* Return: 0 on success.
*/
static int cdx_unregister_device(struct device *dev,
void *data)
{
struct cdx_device *cdx_dev = to_cdx_device(dev);
kfree(cdx_dev->driver_override);
cdx_dev->driver_override = NULL;
/*
* Do not free cdx_dev here as it would be freed in
* cdx_device_release() called from within put_device().
*/
device_del(&cdx_dev->dev);
put_device(&cdx_dev->dev);
return 0;
}
static void cdx_unregister_devices(struct bus_type *bus)
{
/* Reset all the devices attached to cdx bus */
bus_for_each_dev(bus, NULL, NULL, cdx_unregister_device);
}
/**
* cdx_match_one_device - Tell if a CDX device structure has a matching
* CDX device id structure
* @id: single CDX device id structure to match
* @dev: the CDX device structure to match against
*
* Return: matching cdx_device_id structure or NULL if there is no match.
*/
static inline const struct cdx_device_id *
cdx_match_one_device(const struct cdx_device_id *id,
const struct cdx_device *dev)
{
/* Use vendor ID and device ID for matching */
if ((id->vendor == CDX_ANY_ID || id->vendor == dev->vendor) &&
(id->device == CDX_ANY_ID || id->device == dev->device))
return id;
return NULL;
}
/**
* cdx_match_id - See if a CDX device matches a given cdx_id table
* @ids: array of CDX device ID structures to search in
* @dev: the CDX device structure to match against.
*
* Used by a driver to check whether a CDX device is in its list of
* supported devices. Returns the matching cdx_device_id structure or
* NULL if there is no match.
*
* Return: matching cdx_device_id structure or NULL if there is no match.
*/
static inline const struct cdx_device_id *
cdx_match_id(const struct cdx_device_id *ids, struct cdx_device *dev)
{
if (ids) {
while (ids->vendor || ids->device) {
if (cdx_match_one_device(ids, dev))
return ids;
ids++;
}
}
return NULL;
}
/**
* cdx_bus_match - device to driver matching callback
* @dev: the cdx device to match against
* @drv: the device driver to search for matching cdx device
* structures
*
* Return: true on success, false otherwise.
*/
static int cdx_bus_match(struct device *dev, struct device_driver *drv)
{
struct cdx_device *cdx_dev = to_cdx_device(dev);
struct cdx_driver *cdx_drv = to_cdx_driver(drv);
const struct cdx_device_id *found_id = NULL;
const struct cdx_device_id *ids;
ids = cdx_drv->match_id_table;
/* When driver_override is set, only bind to the matching driver */
if (cdx_dev->driver_override && strcmp(cdx_dev->driver_override, drv->name))
return false;
found_id = cdx_match_id(ids, cdx_dev);
if (!found_id)
return false;
do {
/*
* In case override_only was set, enforce driver_override
* matching.
*/
if (!found_id->override_only)
return true;
if (cdx_dev->driver_override)
return true;
ids = found_id + 1;
found_id = cdx_match_id(ids, cdx_dev);
} while (found_id);
return false;
}
static int cdx_probe(struct device *dev)
{
struct cdx_driver *cdx_drv = to_cdx_driver(dev->driver);
struct cdx_device *cdx_dev = to_cdx_device(dev);
int error;
error = cdx_drv->probe(cdx_dev);
if (error) {
dev_err_probe(dev, error, "%s failed\n", __func__);
return error;
}
return 0;
}
static void cdx_remove(struct device *dev)
{
struct cdx_driver *cdx_drv = to_cdx_driver(dev->driver);
struct cdx_device *cdx_dev = to_cdx_device(dev);
if (cdx_drv && cdx_drv->remove)
cdx_drv->remove(cdx_dev);
}
static void cdx_shutdown(struct device *dev)
{
struct cdx_driver *cdx_drv = to_cdx_driver(dev->driver);
struct cdx_device *cdx_dev = to_cdx_device(dev);
if (cdx_drv && cdx_drv->shutdown)
cdx_drv->shutdown(cdx_dev);
}
static int cdx_dma_configure(struct device *dev)
{
struct cdx_device *cdx_dev = to_cdx_device(dev);
u32 input_id = cdx_dev->req_id;
int ret;
ret = of_dma_configure_id(dev, dev->parent->of_node, 0, &input_id);
if (ret && ret != -EPROBE_DEFER) {
dev_err(dev, "of_dma_configure_id() failed\n");
return ret;
}
return 0;
}
/* show configuration fields */
#define cdx_config_attr(field, format_string) \
static ssize_t \
field##_show(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct cdx_device *cdx_dev = to_cdx_device(dev); \
return sysfs_emit(buf, format_string, cdx_dev->field); \
} \
static DEVICE_ATTR_RO(field)
cdx_config_attr(vendor, "0x%04x\n");
cdx_config_attr(device, "0x%04x\n");
static ssize_t remove_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
bool val;
if (kstrtobool(buf, &val) < 0)
return -EINVAL;
if (!val)
return -EINVAL;
if (device_remove_file_self(dev, attr)) {
int ret;
ret = cdx_unregister_device(dev, NULL);
if (ret)
return ret;
}
return count;
}
static DEVICE_ATTR_WO(remove);
static ssize_t reset_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
bool val;
int ret;
if (kstrtobool(buf, &val) < 0)
return -EINVAL;
if (!val)
return -EINVAL;
ret = cdx_dev_reset(dev);
if (ret)
return ret;
return count;
}
static DEVICE_ATTR_WO(reset);
static ssize_t driver_override_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct cdx_device *cdx_dev = to_cdx_device(dev);
int ret;
if (WARN_ON(dev->bus != &cdx_bus_type))
return -EINVAL;
ret = driver_set_override(dev, &cdx_dev->driver_override, buf, count);
if (ret)
return ret;
return count;
}
static ssize_t driver_override_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct cdx_device *cdx_dev = to_cdx_device(dev);
return sysfs_emit(buf, "%s\n", cdx_dev->driver_override);
}
static DEVICE_ATTR_RW(driver_override);
static struct attribute *cdx_dev_attrs[] = {
&dev_attr_remove.attr,
&dev_attr_reset.attr,
&dev_attr_vendor.attr,
&dev_attr_device.attr,
&dev_attr_driver_override.attr,
NULL,
};
ATTRIBUTE_GROUPS(cdx_dev);
static ssize_t rescan_store(struct bus_type *bus,
const char *buf, size_t count)
{
struct cdx_controller *cdx;
unsigned long index;
bool val;
if (kstrtobool(buf, &val) < 0)
return -EINVAL;
if (!val)
return -EINVAL;
/* Unregister all the devices on the bus */
cdx_unregister_devices(&cdx_bus_type);
/* Rescan all the devices */
xa_for_each(&cdx_controllers, index, cdx) {
int ret;
ret = cdx->ops->scan(cdx);
if (ret)
dev_err(cdx->dev, "cdx bus scanning failed\n");
}
return count;
}
static BUS_ATTR_WO(rescan);
static struct attribute *cdx_bus_attrs[] = {
&bus_attr_rescan.attr,
NULL,
};
ATTRIBUTE_GROUPS(cdx_bus);
struct bus_type cdx_bus_type = {
.name = "cdx",
.match = cdx_bus_match,
.probe = cdx_probe,
.remove = cdx_remove,
.shutdown = cdx_shutdown,
.dma_configure = cdx_dma_configure,
.bus_groups = cdx_bus_groups,
.dev_groups = cdx_dev_groups,
};
EXPORT_SYMBOL_GPL(cdx_bus_type);
int __cdx_driver_register(struct cdx_driver *cdx_driver,
struct module *owner)
{
int error;
cdx_driver->driver.owner = owner;
cdx_driver->driver.bus = &cdx_bus_type;
error = driver_register(&cdx_driver->driver);
if (error) {
pr_err("driver_register() failed for %s: %d\n",
cdx_driver->driver.name, error);
return error;
}
return 0;
}
EXPORT_SYMBOL_GPL(__cdx_driver_register);
void cdx_driver_unregister(struct cdx_driver *cdx_driver)
{
driver_unregister(&cdx_driver->driver);
}
EXPORT_SYMBOL_GPL(cdx_driver_unregister);
static void cdx_device_release(struct device *dev)
{
struct cdx_device *cdx_dev = to_cdx_device(dev);
kfree(cdx_dev);
}
int cdx_device_add(struct cdx_dev_params *dev_params)
{
struct cdx_controller *cdx = dev_params->cdx;
struct device *parent = cdx->dev;
struct cdx_device *cdx_dev;
int ret;
cdx_dev = kzalloc(sizeof(*cdx_dev), GFP_KERNEL);
if (!cdx_dev)
return -ENOMEM;
/* Populate resource */
memcpy(cdx_dev->res, dev_params->res, sizeof(struct resource) *
dev_params->res_count);
cdx_dev->res_count = dev_params->res_count;
/* Populate CDX dev params */
cdx_dev->req_id = dev_params->req_id;
cdx_dev->vendor = dev_params->vendor;
cdx_dev->device = dev_params->device;
cdx_dev->bus_num = dev_params->bus_num;
cdx_dev->dev_num = dev_params->dev_num;
cdx_dev->cdx = dev_params->cdx;
cdx_dev->dma_mask = CDX_DEFAULT_DMA_MASK;
/* Initialize generic device */
device_initialize(&cdx_dev->dev);
cdx_dev->dev.parent = parent;
cdx_dev->dev.bus = &cdx_bus_type;
cdx_dev->dev.dma_mask = &cdx_dev->dma_mask;
cdx_dev->dev.release = cdx_device_release;
/* Set Name */
dev_set_name(&cdx_dev->dev, "cdx-%02x:%02x",
((cdx->id << CDX_CONTROLLER_ID_SHIFT) | (cdx_dev->bus_num & CDX_BUS_NUM_MASK)),
cdx_dev->dev_num);
ret = device_add(&cdx_dev->dev);
if (ret) {
dev_err(&cdx_dev->dev,
"cdx device add failed: %d", ret);
goto fail;
}
return 0;
fail:
/*
* Do not free cdx_dev here as it would be freed in
* cdx_device_release() called from put_device().
*/
put_device(&cdx_dev->dev);
return ret;
}
EXPORT_SYMBOL_GPL(cdx_device_add);
int cdx_register_controller(struct cdx_controller *cdx)
{
int ret;
ret = xa_alloc(&cdx_controllers, &cdx->id, cdx,
XA_LIMIT(0, MAX_CDX_CONTROLLERS - 1), GFP_KERNEL);
if (ret) {
dev_err(cdx->dev,
"No free index available. Maximum controllers already registered\n");
cdx->id = (u8)MAX_CDX_CONTROLLERS;
return ret;
}
/* Scan all the devices */
cdx->ops->scan(cdx);
return 0;
}
EXPORT_SYMBOL_GPL(cdx_register_controller);
void cdx_unregister_controller(struct cdx_controller *cdx)
{
if (cdx->id >= MAX_CDX_CONTROLLERS)
return;
device_for_each_child(cdx->dev, NULL, cdx_unregister_device);
xa_erase(&cdx_controllers, cdx->id);
}
EXPORT_SYMBOL_GPL(cdx_unregister_controller);
static int __init cdx_bus_init(void)
{
return bus_register(&cdx_bus_type);
}
postcore_initcall(cdx_bus_init);

62
drivers/cdx/cdx.h Normal file
View File

@ -0,0 +1,62 @@
/* SPDX-License-Identifier: GPL-2.0
*
* Header file for the CDX Bus
*
* Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
*/
#ifndef _CDX_H_
#define _CDX_H_
#include <linux/cdx/cdx_bus.h>
/**
* struct cdx_dev_params - CDX device parameters
* @cdx: CDX controller associated with the device
* @parent: Associated CDX controller
* @vendor: Vendor ID for CDX device
* @device: Device ID for CDX device
* @bus_num: Bus number for this CDX device
* @dev_num: Device number for this device
* @res: array of MMIO region entries
* @res_count: number of valid MMIO regions
* @req_id: Requestor ID associated with CDX device
*/
struct cdx_dev_params {
struct cdx_controller *cdx;
u16 vendor;
u16 device;
u8 bus_num;
u8 dev_num;
struct resource res[MAX_CDX_DEV_RESOURCES];
u8 res_count;
u32 req_id;
};
/**
* cdx_register_controller - Register a CDX controller and its ports
* on the CDX bus.
* @cdx: The CDX controller to register
*
* Return: -errno on failure, 0 on success.
*/
int cdx_register_controller(struct cdx_controller *cdx);
/**
* cdx_unregister_controller - Unregister a CDX controller
* @cdx: The CDX controller to unregister
*/
void cdx_unregister_controller(struct cdx_controller *cdx);
/**
* cdx_device_add - Add a CDX device. This function adds a CDX device
* on the CDX bus as per the device parameters provided
* by caller. It also creates and registers an associated
* Linux generic device.
* @dev_params: device parameters associated with the device to be created.
*
* Return: -errno on failure, 0 on success.
*/
int cdx_device_add(struct cdx_dev_params *dev_params);
#endif /* _CDX_H_ */

View File

@ -0,0 +1,31 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# CDX controller configuration
#
# Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
#
if CDX_BUS
config CDX_CONTROLLER
tristate "CDX bus controller"
select REMOTEPROC
select RPMSG
help
CDX controller drives the CDX bus. It interacts with
firmware to get the hardware devices and registers with
the CDX bus. Say Y to enable the CDX hardware driver.
If unsure, say N.
config MCDI_LOGGING
bool "MCDI Logging for the CDX controller"
depends on CDX_CONTROLLER
help
Enable MCDI Logging for
the CDX Controller for debug
purpose.
If unsure, say N.
endif

View File

@ -0,0 +1,9 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for CDX controller drivers
#
# Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
#
obj-$(CONFIG_CDX_CONTROLLER) += cdx-controller.o
cdx-controller-objs := cdx_controller.o cdx_rpmsg.o mcdi.o mcdi_functions.o

View File

@ -0,0 +1,90 @@
/* SPDX-License-Identifier: GPL-2.0
*
* Copyright 2005-2006 Fen Systems Ltd.
* Copyright 2006-2013 Solarflare Communications Inc.
* Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
*/
#ifndef CDX_BITFIELD_H
#define CDX_BITFIELD_H
#include <linux/bitfield.h>
/* Lowest bit numbers and widths */
#define CDX_DWORD_LBN 0
#define CDX_DWORD_WIDTH 32
/* Specified attribute (e.g. LBN) of the specified field */
#define CDX_VAL(field, attribute) field ## _ ## attribute
/* Low bit number of the specified field */
#define CDX_LOW_BIT(field) CDX_VAL(field, LBN)
/* Bit width of the specified field */
#define CDX_WIDTH(field) CDX_VAL(field, WIDTH)
/* High bit number of the specified field */
#define CDX_HIGH_BIT(field) (CDX_LOW_BIT(field) + CDX_WIDTH(field) - 1)
/* A doubleword (i.e. 4 byte) datatype - little-endian in HW */
struct cdx_dword {
__le32 cdx_u32;
};
/* Value expanders for printk */
#define CDX_DWORD_VAL(dword) \
((unsigned int)le32_to_cpu((dword).cdx_u32))
/*
* Extract bit field portion [low,high) from the 32-bit little-endian
* element which contains bits [min,max)
*/
#define CDX_DWORD_FIELD(dword, field) \
(FIELD_GET(GENMASK(CDX_HIGH_BIT(field), CDX_LOW_BIT(field)), \
le32_to_cpu((dword).cdx_u32)))
/*
* Creates the portion of the named bit field that lies within the
* range [min,max).
*/
#define CDX_INSERT_FIELD(field, value) \
(FIELD_PREP(GENMASK(CDX_HIGH_BIT(field), \
CDX_LOW_BIT(field)), value))
/*
* Creates the portion of the named bit fields that lie within the
* range [min,max).
*/
#define CDX_INSERT_FIELDS(field1, value1, \
field2, value2, \
field3, value3, \
field4, value4, \
field5, value5, \
field6, value6, \
field7, value7) \
(CDX_INSERT_FIELD(field1, (value1)) | \
CDX_INSERT_FIELD(field2, (value2)) | \
CDX_INSERT_FIELD(field3, (value3)) | \
CDX_INSERT_FIELD(field4, (value4)) | \
CDX_INSERT_FIELD(field5, (value5)) | \
CDX_INSERT_FIELD(field6, (value6)) | \
CDX_INSERT_FIELD(field7, (value7)))
#define CDX_POPULATE_DWORD(dword, ...) \
(dword).cdx_u32 = cpu_to_le32(CDX_INSERT_FIELDS(__VA_ARGS__))
/* Populate a dword field with various numbers of arguments */
#define CDX_POPULATE_DWORD_7 CDX_POPULATE_DWORD
#define CDX_POPULATE_DWORD_6(dword, ...) \
CDX_POPULATE_DWORD_7(dword, CDX_DWORD, 0, __VA_ARGS__)
#define CDX_POPULATE_DWORD_5(dword, ...) \
CDX_POPULATE_DWORD_6(dword, CDX_DWORD, 0, __VA_ARGS__)
#define CDX_POPULATE_DWORD_4(dword, ...) \
CDX_POPULATE_DWORD_5(dword, CDX_DWORD, 0, __VA_ARGS__)
#define CDX_POPULATE_DWORD_3(dword, ...) \
CDX_POPULATE_DWORD_4(dword, CDX_DWORD, 0, __VA_ARGS__)
#define CDX_POPULATE_DWORD_2(dword, ...) \
CDX_POPULATE_DWORD_3(dword, CDX_DWORD, 0, __VA_ARGS__)
#define CDX_POPULATE_DWORD_1(dword, ...) \
CDX_POPULATE_DWORD_2(dword, CDX_DWORD, 0, __VA_ARGS__)
#define CDX_SET_DWORD(dword) \
CDX_POPULATE_DWORD_1(dword, CDX_DWORD, 0xffffffff)
#endif /* CDX_BITFIELD_H */

View File

@ -0,0 +1,230 @@
// SPDX-License-Identifier: GPL-2.0
/*
* CDX host controller driver for AMD versal-net platform.
*
* Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
*/
#include <linux/of_platform.h>
#include <linux/slab.h>
#include <linux/cdx/cdx_bus.h>
#include "cdx_controller.h"
#include "../cdx.h"
#include "mcdi_functions.h"
#include "mcdi.h"
static unsigned int cdx_mcdi_rpc_timeout(struct cdx_mcdi *cdx, unsigned int cmd)
{
return MCDI_RPC_TIMEOUT;
}
static void cdx_mcdi_request(struct cdx_mcdi *cdx,
const struct cdx_dword *hdr, size_t hdr_len,
const struct cdx_dword *sdu, size_t sdu_len)
{
if (cdx_rpmsg_send(cdx, hdr, hdr_len, sdu, sdu_len))
dev_err(&cdx->rpdev->dev, "Failed to send rpmsg data\n");
}
static const struct cdx_mcdi_ops mcdi_ops = {
.mcdi_rpc_timeout = cdx_mcdi_rpc_timeout,
.mcdi_request = cdx_mcdi_request,
};
void cdx_rpmsg_post_probe(struct cdx_controller *cdx)
{
/* Register CDX controller with CDX bus driver */
if (cdx_register_controller(cdx))
dev_err(cdx->dev, "Failed to register CDX controller\n");
}
void cdx_rpmsg_pre_remove(struct cdx_controller *cdx)
{
cdx_unregister_controller(cdx);
cdx_mcdi_wait_for_quiescence(cdx->priv, MCDI_RPC_TIMEOUT);
}
static int cdx_configure_device(struct cdx_controller *cdx,
u8 bus_num, u8 dev_num,
struct cdx_device_config *dev_config)
{
int ret = 0;
switch (dev_config->type) {
case CDX_DEV_RESET_CONF:
ret = cdx_mcdi_reset_device(cdx->priv, bus_num, dev_num);
break;
default:
ret = -EINVAL;
}
return ret;
}
static int cdx_scan_devices(struct cdx_controller *cdx)
{
struct cdx_mcdi *cdx_mcdi = cdx->priv;
u8 bus_num, dev_num, num_cdx_bus;
int ret;
/* MCDI FW Read: Fetch the number of CDX buses on this controller */
ret = cdx_mcdi_get_num_buses(cdx_mcdi);
if (ret < 0) {
dev_err(cdx->dev,
"Get number of CDX buses failed: %d\n", ret);
return ret;
}
num_cdx_bus = (u8)ret;
for (bus_num = 0; bus_num < num_cdx_bus; bus_num++) {
u8 num_cdx_dev;
/* MCDI FW Read: Fetch the number of devices present */
ret = cdx_mcdi_get_num_devs(cdx_mcdi, bus_num);
if (ret < 0) {
dev_err(cdx->dev,
"Get devices on CDX bus %d failed: %d\n", bus_num, ret);
continue;
}
num_cdx_dev = (u8)ret;
for (dev_num = 0; dev_num < num_cdx_dev; dev_num++) {
struct cdx_dev_params dev_params;
/* MCDI FW: Get the device config */
ret = cdx_mcdi_get_dev_config(cdx_mcdi, bus_num,
dev_num, &dev_params);
if (ret) {
dev_err(cdx->dev,
"CDX device config get failed for %d(bus):%d(dev), %d\n",
bus_num, dev_num, ret);
continue;
}
dev_params.cdx = cdx;
/* Add the device to the cdx bus */
ret = cdx_device_add(&dev_params);
if (ret) {
dev_err(cdx->dev, "registering cdx dev: %d failed: %d\n",
dev_num, ret);
continue;
}
dev_dbg(cdx->dev, "CDX dev: %d on cdx bus: %d created\n",
dev_num, bus_num);
}
}
return 0;
}
static struct cdx_ops cdx_ops = {
.scan = cdx_scan_devices,
.dev_configure = cdx_configure_device,
};
static int xlnx_cdx_probe(struct platform_device *pdev)
{
struct cdx_controller *cdx;
struct cdx_mcdi *cdx_mcdi;
int ret;
cdx_mcdi = kzalloc(sizeof(*cdx_mcdi), GFP_KERNEL);
if (!cdx_mcdi)
return -ENOMEM;
/* Store the MCDI ops */
cdx_mcdi->mcdi_ops = &mcdi_ops;
/* MCDI FW: Initialize the FW path */
ret = cdx_mcdi_init(cdx_mcdi);
if (ret) {
dev_err_probe(&pdev->dev, ret, "MCDI Initialization failed\n");
goto mcdi_init_fail;
}
cdx = kzalloc(sizeof(*cdx), GFP_KERNEL);
if (!cdx) {
ret = -ENOMEM;
goto cdx_alloc_fail;
}
platform_set_drvdata(pdev, cdx);
cdx->dev = &pdev->dev;
cdx->priv = cdx_mcdi;
cdx->ops = &cdx_ops;
ret = cdx_setup_rpmsg(pdev);
if (ret) {
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev, "Failed to register CDX RPMsg transport\n");
goto cdx_rpmsg_fail;
}
dev_info(&pdev->dev, "Successfully registered CDX controller with RPMsg as transport\n");
return 0;
cdx_rpmsg_fail:
kfree(cdx);
cdx_alloc_fail:
cdx_mcdi_finish(cdx_mcdi);
mcdi_init_fail:
kfree(cdx_mcdi);
return ret;
}
static int xlnx_cdx_remove(struct platform_device *pdev)
{
struct cdx_controller *cdx = platform_get_drvdata(pdev);
struct cdx_mcdi *cdx_mcdi = cdx->priv;
cdx_destroy_rpmsg(pdev);
kfree(cdx);
cdx_mcdi_finish(cdx_mcdi);
kfree(cdx_mcdi);
return 0;
}
static const struct of_device_id cdx_match_table[] = {
{.compatible = "xlnx,versal-net-cdx",},
{ },
};
MODULE_DEVICE_TABLE(of, cdx_match_table);
static struct platform_driver cdx_pdriver = {
.driver = {
.name = "cdx-controller",
.pm = NULL,
.of_match_table = cdx_match_table,
},
.probe = xlnx_cdx_probe,
.remove = xlnx_cdx_remove,
};
static int __init cdx_controller_init(void)
{
int ret;
ret = platform_driver_register(&cdx_pdriver);
if (ret)
pr_err("platform_driver_register() failed: %d\n", ret);
return ret;
}
static void __exit cdx_controller_exit(void)
{
platform_driver_unregister(&cdx_pdriver);
}
module_init(cdx_controller_init);
module_exit(cdx_controller_exit);
MODULE_AUTHOR("AMD Inc.");
MODULE_DESCRIPTION("CDX controller for AMD devices");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,30 @@
/* SPDX-License-Identifier: GPL-2.0
*
* Header file for the CDX Controller
*
* Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
*/
#ifndef _CDX_CONTROLLER_H_
#define _CDX_CONTROLLER_H_
#include <linux/cdx/cdx_bus.h>
#include "mcdi_functions.h"
void cdx_rpmsg_post_probe(struct cdx_controller *cdx);
void cdx_rpmsg_pre_remove(struct cdx_controller *cdx);
int cdx_rpmsg_send(struct cdx_mcdi *cdx_mcdi,
const struct cdx_dword *hdr, size_t hdr_len,
const struct cdx_dword *sdu, size_t sdu_len);
void cdx_rpmsg_read_resp(struct cdx_mcdi *cdx_mcdi,
struct cdx_dword *outbuf, size_t offset,
size_t outlen);
int cdx_setup_rpmsg(struct platform_device *pdev);
void cdx_destroy_rpmsg(struct platform_device *pdev);
#endif /* _CDX_CONT_PRIV_H_ */

View File

@ -0,0 +1,202 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Platform driver for CDX bus.
*
* Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
*/
#include <linux/rpmsg.h>
#include <linux/remoteproc.h>
#include <linux/of_platform.h>
#include <linux/cdx/cdx_bus.h>
#include <linux/module.h>
#include "../cdx.h"
#include "cdx_controller.h"
#include "mcdi_functions.h"
#include "mcdi.h"
static struct rpmsg_device_id cdx_rpmsg_id_table[] = {
{ .name = "mcdi_ipc" },
{ },
};
MODULE_DEVICE_TABLE(rpmsg, cdx_rpmsg_id_table);
int cdx_rpmsg_send(struct cdx_mcdi *cdx_mcdi,
const struct cdx_dword *hdr, size_t hdr_len,
const struct cdx_dword *sdu, size_t sdu_len)
{
unsigned char *send_buf;
int ret;
send_buf = kzalloc(hdr_len + sdu_len, GFP_KERNEL);
if (!send_buf)
return -ENOMEM;
memcpy(send_buf, hdr, hdr_len);
memcpy(send_buf + hdr_len, sdu, sdu_len);
ret = rpmsg_send(cdx_mcdi->ept, send_buf, hdr_len + sdu_len);
kfree(send_buf);
return ret;
}
static int cdx_attach_to_rproc(struct platform_device *pdev)
{
struct device_node *r5_core_node;
struct cdx_controller *cdx_c;
struct cdx_mcdi *cdx_mcdi;
struct device *dev;
struct rproc *rp;
int ret;
dev = &pdev->dev;
cdx_c = platform_get_drvdata(pdev);
cdx_mcdi = cdx_c->priv;
r5_core_node = of_parse_phandle(dev->of_node, "xlnx,rproc", 0);
if (!r5_core_node) {
dev_err(&pdev->dev, "xlnx,rproc: invalid phandle\n");
return -EINVAL;
}
rp = rproc_get_by_phandle(r5_core_node->phandle);
if (!rp) {
ret = -EPROBE_DEFER;
goto pdev_err;
}
/* Attach to remote processor */
ret = rproc_boot(rp);
if (ret) {
dev_err(&pdev->dev, "Failed to attach to remote processor\n");
rproc_put(rp);
goto pdev_err;
}
cdx_mcdi->r5_rproc = rp;
pdev_err:
of_node_put(r5_core_node);
return ret;
}
static void cdx_detach_to_r5(struct platform_device *pdev)
{
struct cdx_controller *cdx_c;
struct cdx_mcdi *cdx_mcdi;
cdx_c = platform_get_drvdata(pdev);
cdx_mcdi = cdx_c->priv;
rproc_detach(cdx_mcdi->r5_rproc);
rproc_put(cdx_mcdi->r5_rproc);
}
static int cdx_rpmsg_cb(struct rpmsg_device *rpdev, void *data,
int len, void *priv, u32 src)
{
struct cdx_controller *cdx_c = dev_get_drvdata(&rpdev->dev);
struct cdx_mcdi *cdx_mcdi = cdx_c->priv;
if (len > MCDI_BUF_LEN)
return -EINVAL;
cdx_mcdi_process_cmd(cdx_mcdi, (struct cdx_dword *)data, len);
return 0;
}
static void cdx_rpmsg_post_probe_work(struct work_struct *work)
{
struct cdx_controller *cdx_c;
struct cdx_mcdi *cdx_mcdi;
cdx_mcdi = container_of(work, struct cdx_mcdi, work);
cdx_c = dev_get_drvdata(&cdx_mcdi->rpdev->dev);
cdx_rpmsg_post_probe(cdx_c);
}
static int cdx_rpmsg_probe(struct rpmsg_device *rpdev)
{
struct rpmsg_channel_info chinfo = {0};
struct cdx_controller *cdx_c;
struct cdx_mcdi *cdx_mcdi;
cdx_c = (struct cdx_controller *)cdx_rpmsg_id_table[0].driver_data;
cdx_mcdi = cdx_c->priv;
chinfo.src = RPMSG_ADDR_ANY;
chinfo.dst = rpdev->dst;
strscpy(chinfo.name, cdx_rpmsg_id_table[0].name,
strlen(cdx_rpmsg_id_table[0].name));
cdx_mcdi->ept = rpmsg_create_ept(rpdev, cdx_rpmsg_cb, NULL, chinfo);
if (!cdx_mcdi->ept) {
dev_err_probe(&rpdev->dev, -ENXIO,
"Failed to create ept for channel %s\n",
chinfo.name);
return -EINVAL;
}
cdx_mcdi->rpdev = rpdev;
dev_set_drvdata(&rpdev->dev, cdx_c);
schedule_work(&cdx_mcdi->work);
return 0;
}
static void cdx_rpmsg_remove(struct rpmsg_device *rpdev)
{
struct cdx_controller *cdx_c = dev_get_drvdata(&rpdev->dev);
struct cdx_mcdi *cdx_mcdi = cdx_c->priv;
flush_work(&cdx_mcdi->work);
cdx_rpmsg_pre_remove(cdx_c);
rpmsg_destroy_ept(cdx_mcdi->ept);
dev_set_drvdata(&rpdev->dev, NULL);
}
static struct rpmsg_driver cdx_rpmsg_driver = {
.drv.name = KBUILD_MODNAME,
.id_table = cdx_rpmsg_id_table,
.probe = cdx_rpmsg_probe,
.remove = cdx_rpmsg_remove,
.callback = cdx_rpmsg_cb,
};
int cdx_setup_rpmsg(struct platform_device *pdev)
{
struct cdx_controller *cdx_c;
struct cdx_mcdi *cdx_mcdi;
int ret;
/* Attach to remote processor */
ret = cdx_attach_to_rproc(pdev);
if (ret)
return ret;
cdx_c = platform_get_drvdata(pdev);
cdx_mcdi = cdx_c->priv;
/* Register RPMsg driver */
cdx_rpmsg_id_table[0].driver_data = (kernel_ulong_t)cdx_c;
INIT_WORK(&cdx_mcdi->work, cdx_rpmsg_post_probe_work);
ret = register_rpmsg_driver(&cdx_rpmsg_driver);
if (ret) {
dev_err(&pdev->dev,
"Failed to register cdx RPMsg driver: %d\n", ret);
cdx_detach_to_r5(pdev);
}
return ret;
}
void cdx_destroy_rpmsg(struct platform_device *pdev)
{
unregister_rpmsg_driver(&cdx_rpmsg_driver);
cdx_detach_to_r5(pdev);
}

View File

@ -0,0 +1,590 @@
/* SPDX-License-Identifier: GPL-2.0
*
* Driver for AMD network controllers and boards
*
* Copyright (C) 2021, Xilinx, Inc.
* Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
*/
#ifndef MC_CDX_PCOL_H
#define MC_CDX_PCOL_H
/* The current version of the MCDI protocol. */
#define MCDI_PCOL_VERSION 2
/*
* Each MCDI request starts with an MCDI_HEADER, which is a 32bit
* structure, filled in by the client.
*
* 0 7 8 16 20 22 23 24 31
* | CODE | R | LEN | SEQ | Rsvd | E | R | XFLAGS |
* | | |
* | | \--- Response
* | \------- Error
* \------------------------------ Resync (always set)
*
* The client writes its request into MC shared memory, and rings the
* doorbell. Each request is completed either by the MC writing
* back into shared memory, or by writing out an event.
*
* All MCDI commands support completion by shared memory response. Each
* request may also contain additional data (accounted for by HEADER.LEN),
* and some responses may also contain additional data (again, accounted
* for by HEADER.LEN).
*
* Some MCDI commands support completion by event, in which any associated
* response data is included in the event.
*
* The protocol requires one response to be delivered for every request; a
* request should not be sent unless the response for the previous request
* has been received (either by polling shared memory, or by receiving
* an event).
*/
/** Request/Response structure */
#define MCDI_HEADER_OFST 0
#define MCDI_HEADER_CODE_LBN 0
#define MCDI_HEADER_CODE_WIDTH 7
#define MCDI_HEADER_RESYNC_LBN 7
#define MCDI_HEADER_RESYNC_WIDTH 1
#define MCDI_HEADER_DATALEN_LBN 8
#define MCDI_HEADER_DATALEN_WIDTH 8
#define MCDI_HEADER_SEQ_LBN 16
#define MCDI_HEADER_SEQ_WIDTH 4
#define MCDI_HEADER_RSVD_LBN 20
#define MCDI_HEADER_RSVD_WIDTH 1
#define MCDI_HEADER_NOT_EPOCH_LBN 21
#define MCDI_HEADER_NOT_EPOCH_WIDTH 1
#define MCDI_HEADER_ERROR_LBN 22
#define MCDI_HEADER_ERROR_WIDTH 1
#define MCDI_HEADER_RESPONSE_LBN 23
#define MCDI_HEADER_RESPONSE_WIDTH 1
#define MCDI_HEADER_XFLAGS_LBN 24
#define MCDI_HEADER_XFLAGS_WIDTH 8
/* Request response using event */
#define MCDI_HEADER_XFLAGS_EVREQ 0x01
/* Request (and signal) early doorbell return */
#define MCDI_HEADER_XFLAGS_DBRET 0x02
/* Maximum number of payload bytes */
#define MCDI_CTL_SDU_LEN_MAX_V2 0x400
#define MCDI_CTL_SDU_LEN_MAX MCDI_CTL_SDU_LEN_MAX_V2
/*
* The MC can generate events for two reasons:
* - To advance a shared memory request if XFLAGS_EVREQ was set
* - As a notification (link state, i2c event), controlled
* via MC_CMD_LOG_CTRL
*
* Both events share a common structure:
*
* 0 32 33 36 44 52 60
* | Data | Cont | Level | Src | Code | Rsvd |
* |
* \ There is another event pending in this notification
*
* If Code==CMDDONE, then the fields are further interpreted as:
*
* - LEVEL==INFO Command succeeded
* - LEVEL==ERR Command failed
*
* 0 8 16 24 32
* | Seq | Datalen | Errno | Rsvd |
*
* These fields are taken directly out of the standard MCDI header, i.e.,
* LEVEL==ERR, Datalen == 0 => Reboot
*
* Events can be squirted out of the UART (using LOG_CTRL) without a
* MCDI header. An event can be distinguished from a MCDI response by
* examining the first byte which is 0xc0. This corresponds to the
* non-existent MCDI command MC_CMD_DEBUG_LOG.
*
* 0 7 8
* | command | Resync | = 0xc0
*
* Since the event is written in big-endian byte order, this works
* providing bits 56-63 of the event are 0xc0.
*
* 56 60 63
* | Rsvd | Code | = 0xc0
*
* Which means for convenience the event code is 0xc for all MC
* generated events.
*/
/*
* the errno value may be followed by the (0-based) number of the
* first argument that could not be processed.
*/
#define MC_CMD_ERR_ARG_OFST 4
/* MC_CMD_ERR MCDI error codes. */
/* Operation not permitted. */
#define MC_CMD_ERR_EPERM 0x1
/* Non-existent command target */
#define MC_CMD_ERR_ENOENT 0x2
/* assert() has killed the MC */
#define MC_CMD_ERR_EINTR 0x4
/* I/O failure */
#define MC_CMD_ERR_EIO 0x5
/* Already exists */
#define MC_CMD_ERR_EEXIST 0x6
/* Try again */
#define MC_CMD_ERR_EAGAIN 0xb
/* Out of memory */
#define MC_CMD_ERR_ENOMEM 0xc
/* Caller does not hold required locks */
#define MC_CMD_ERR_EACCES 0xd
/* Resource is currently unavailable (e.g. lock contention) */
#define MC_CMD_ERR_EBUSY 0x10
/* No such device */
#define MC_CMD_ERR_ENODEV 0x13
/* Invalid argument to target */
#define MC_CMD_ERR_EINVAL 0x16
/* No space */
#define MC_CMD_ERR_ENOSPC 0x1c
/* Read-only */
#define MC_CMD_ERR_EROFS 0x1e
/* Broken pipe */
#define MC_CMD_ERR_EPIPE 0x20
/* Out of range */
#define MC_CMD_ERR_ERANGE 0x22
/* Non-recursive resource is already acquired */
#define MC_CMD_ERR_EDEADLK 0x23
/* Operation not implemented */
#define MC_CMD_ERR_ENOSYS 0x26
/* Operation timed out */
#define MC_CMD_ERR_ETIME 0x3e
/* Link has been severed */
#define MC_CMD_ERR_ENOLINK 0x43
/* Protocol error */
#define MC_CMD_ERR_EPROTO 0x47
/* Bad message */
#define MC_CMD_ERR_EBADMSG 0x4a
/* Operation not supported */
#define MC_CMD_ERR_ENOTSUP 0x5f
/* Address not available */
#define MC_CMD_ERR_EADDRNOTAVAIL 0x63
/* Not connected */
#define MC_CMD_ERR_ENOTCONN 0x6b
/* Operation already in progress */
#define MC_CMD_ERR_EALREADY 0x72
/* Stale handle. The handle references resource that no longer exists */
#define MC_CMD_ERR_ESTALE 0x74
/* Resource allocation failed. */
#define MC_CMD_ERR_ALLOC_FAIL 0x1000
/* V-adaptor not found. */
#define MC_CMD_ERR_NO_VADAPTOR 0x1001
/* EVB port not found. */
#define MC_CMD_ERR_NO_EVB_PORT 0x1002
/* V-switch not found. */
#define MC_CMD_ERR_NO_VSWITCH 0x1003
/* Too many VLAN tags. */
#define MC_CMD_ERR_VLAN_LIMIT 0x1004
/* Bad PCI function number. */
#define MC_CMD_ERR_BAD_PCI_FUNC 0x1005
/* Invalid VLAN mode. */
#define MC_CMD_ERR_BAD_VLAN_MODE 0x1006
/* Invalid v-switch type. */
#define MC_CMD_ERR_BAD_VSWITCH_TYPE 0x1007
/* Invalid v-port type. */
#define MC_CMD_ERR_BAD_VPORT_TYPE 0x1008
/* MAC address exists. */
#define MC_CMD_ERR_MAC_EXIST 0x1009
/* Slave core not present */
#define MC_CMD_ERR_SLAVE_NOT_PRESENT 0x100a
/* The datapath is disabled. */
#define MC_CMD_ERR_DATAPATH_DISABLED 0x100b
/* The requesting client is not a function */
#define MC_CMD_ERR_CLIENT_NOT_FN 0x100c
/*
* The requested operation might require the command to be passed between
* MCs, and the transport doesn't support that. Should only ever been seen over
* the UART.
*/
#define MC_CMD_ERR_NO_PRIVILEGE 0x1013
/*
* Workaround 26807 could not be turned on/off because some functions
* have already installed filters. See the comment at
* MC_CMD_WORKAROUND_BUG26807. May also returned for other operations such as
* sub-variant switching.
*/
#define MC_CMD_ERR_FILTERS_PRESENT 0x1014
/* The clock whose frequency you've attempted to set doesn't exist */
#define MC_CMD_ERR_NO_CLOCK 0x1015
/*
* Returned by MC_CMD_TESTASSERT if the action that should have caused an
* assertion failed to do so.
*/
#define MC_CMD_ERR_UNREACHABLE 0x1016
/*
* This command needs to be processed in the background but there were no
* resources to do so. Send it again after a command has completed.
*/
#define MC_CMD_ERR_QUEUE_FULL 0x1017
/*
* The operation could not be completed because the PCIe link has gone
* away. This error code is never expected to be returned over the TLP
* transport.
*/
#define MC_CMD_ERR_NO_PCIE 0x1018
/*
* The operation could not be completed because the datapath has gone
* away. This is distinct from MC_CMD_ERR_DATAPATH_DISABLED in that the
* datapath absence may be temporary
*/
#define MC_CMD_ERR_NO_DATAPATH 0x1019
/* The operation could not complete because some VIs are allocated */
#define MC_CMD_ERR_VIS_PRESENT 0x101a
/*
* The operation could not complete because some PIO buffers are
* allocated
*/
#define MC_CMD_ERR_PIOBUFS_PRESENT 0x101b
/***********************************/
/*
* MC_CMD_CDX_BUS_ENUM_BUSES
* CDX bus hosts devices (functions) that are implemented using the Composable
* DMA subsystem and directly mapped into the memory space of the FGPA PSX
* Application Processors (APUs). As such, they only apply to the PSX APU side,
* not the host (PCIe). Unlike PCIe, these devices have no native configuration
* space or enumeration mechanism, so this message set provides a minimal
* interface for discovery and management (bus reset, FLR, BME) of such
* devices. This command returns the number of CDX buses present in the system.
*/
#define MC_CMD_CDX_BUS_ENUM_BUSES 0x1
#define MC_CMD_CDX_BUS_ENUM_BUSES_MSGSET 0x1
#undef MC_CMD_0x1_PRIVILEGE_CTG
#define MC_CMD_0x1_PRIVILEGE_CTG SRIOV_CTG_ADMIN
/* MC_CMD_CDX_BUS_ENUM_BUSES_IN msgrequest */
#define MC_CMD_CDX_BUS_ENUM_BUSES_IN_LEN 0
/* MC_CMD_CDX_BUS_ENUM_BUSES_OUT msgresponse */
#define MC_CMD_CDX_BUS_ENUM_BUSES_OUT_LEN 4
/*
* Number of CDX buses present in the system. Buses are numbered 0 to
* BUS_COUNT-1
*/
#define MC_CMD_CDX_BUS_ENUM_BUSES_OUT_BUS_COUNT_OFST 0
#define MC_CMD_CDX_BUS_ENUM_BUSES_OUT_BUS_COUNT_LEN 4
/***********************************/
/*
* MC_CMD_CDX_BUS_ENUM_DEVICES
* Enumerate CDX bus devices on a given bus
*/
#define MC_CMD_CDX_BUS_ENUM_DEVICES 0x2
#define MC_CMD_CDX_BUS_ENUM_DEVICES_MSGSET 0x2
#undef MC_CMD_0x2_PRIVILEGE_CTG
#define MC_CMD_0x2_PRIVILEGE_CTG SRIOV_CTG_ADMIN
/* MC_CMD_CDX_BUS_ENUM_DEVICES_IN msgrequest */
#define MC_CMD_CDX_BUS_ENUM_DEVICES_IN_LEN 4
/*
* Bus number to enumerate, in range 0 to BUS_COUNT-1, as returned by
* MC_CMD_CDX_BUS_ENUM_BUSES_OUT
*/
#define MC_CMD_CDX_BUS_ENUM_DEVICES_IN_BUS_OFST 0
#define MC_CMD_CDX_BUS_ENUM_DEVICES_IN_BUS_LEN 4
/* MC_CMD_CDX_BUS_ENUM_DEVICES_OUT msgresponse */
#define MC_CMD_CDX_BUS_ENUM_DEVICES_OUT_LEN 4
/*
* Number of devices present on the bus. Devices on the bus are numbered 0 to
* DEVICE_COUNT-1. Returns EAGAIN if number of devices unknown or if the target
* devices are not ready (e.g. undergoing a bus reset)
*/
#define MC_CMD_CDX_BUS_ENUM_DEVICES_OUT_DEVICE_COUNT_OFST 0
#define MC_CMD_CDX_BUS_ENUM_DEVICES_OUT_DEVICE_COUNT_LEN 4
/***********************************/
/*
* MC_CMD_CDX_BUS_GET_DEVICE_CONFIG
* Returns device identification and MMIO/MSI resource data for a CDX device.
* The expected usage is for the caller to first retrieve the number of devices
* on the bus using MC_CMD_BUS_ENUM_DEVICES, then loop through the range (0,
* DEVICE_COUNT - 1), retrieving device resource data. May return EAGAIN if the
* number of exposed devices or device resources change during enumeration (due
* to e.g. a PL reload / bus reset), in which case the caller is expected to
* restart the enumeration loop. MMIO addresses are specified in terms of bus
* addresses (prior to any potential IOMMU translation). For versal-net, these
* are equivalent to APU physical addresses. Implementation note - for this to
* work, the implementation needs to keep state (generation count) per client.
*/
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG 0x3
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_MSGSET 0x3
#undef MC_CMD_0x3_PRIVILEGE_CTG
#define MC_CMD_0x3_PRIVILEGE_CTG SRIOV_CTG_ADMIN
/* MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_IN msgrequest */
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_IN_LEN 8
/* Device bus number, in range 0 to BUS_COUNT-1 */
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_IN_BUS_OFST 0
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_IN_BUS_LEN 4
/* Device number relative to the bus, in range 0 to DEVICE_COUNT-1 for that bus */
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_IN_DEVICE_OFST 4
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_IN_DEVICE_LEN 4
/* MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT msgresponse */
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_LEN 88
/* 16-bit Vendor identifier, compliant with PCI-SIG VendorID assignment. */
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_VENDOR_ID_OFST 0
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_VENDOR_ID_LEN 2
/* 16-bit Device ID assigned by the vendor */
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_DEVICE_ID_OFST 2
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_DEVICE_ID_LEN 2
/*
* 16-bit Subsystem Vendor ID, , compliant with PCI-SIG VendorID assignment.
* For further device differentiation, as required. 0 if unused.
*/
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_SUBSYS_VENDOR_ID_OFST 4
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_SUBSYS_VENDOR_ID_LEN 2
/*
* 16-bit Subsystem Device ID assigned by the vendor. For further device
* differentiation, as required. 0 if unused.
*/
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_SUBSYS_DEVICE_ID_OFST 6
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_SUBSYS_DEVICE_ID_LEN 2
/* 24-bit Device Class code, compliant with PCI-SIG Device Class codes */
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_DEVICE_CLASS_OFST 8
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_DEVICE_CLASS_LEN 3
/* 8-bit vendor-assigned revision */
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_DEVICE_REVISION_OFST 11
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_DEVICE_REVISION_LEN 1
/* Reserved (alignment) */
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_RESERVED_OFST 12
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_RESERVED_LEN 4
/* MMIO region 0 base address (bus address), 0 if unused */
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_BASE_OFST 16
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_BASE_LEN 8
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_BASE_LO_OFST 16
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_BASE_LO_LEN 4
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_BASE_LO_LBN 128
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_BASE_LO_WIDTH 32
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_BASE_HI_OFST 20
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_BASE_HI_LEN 4
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_BASE_HI_LBN 160
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_BASE_HI_WIDTH 32
/* MMIO region 0 size, 0 if unused */
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_SIZE_OFST 24
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_SIZE_LEN 8
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_SIZE_LO_OFST 24
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_SIZE_LO_LEN 4
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_SIZE_LO_LBN 192
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_SIZE_LO_WIDTH 32
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_SIZE_HI_OFST 28
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_SIZE_HI_LEN 4
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_SIZE_HI_LBN 224
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_SIZE_HI_WIDTH 32
/* MMIO region 1 base address (bus address), 0 if unused */
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_BASE_OFST 32
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_BASE_LEN 8
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_BASE_LO_OFST 32
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_BASE_LO_LEN 4
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_BASE_LO_LBN 256
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_BASE_LO_WIDTH 32
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_BASE_HI_OFST 36
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_BASE_HI_LEN 4
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_BASE_HI_LBN 288
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_BASE_HI_WIDTH 32
/* MMIO region 1 size, 0 if unused */
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_SIZE_OFST 40
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_SIZE_LEN 8
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_SIZE_LO_OFST 40
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_SIZE_LO_LEN 4
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_SIZE_LO_LBN 320
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_SIZE_LO_WIDTH 32
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_SIZE_HI_OFST 44
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_SIZE_HI_LEN 4
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_SIZE_HI_LBN 352
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_SIZE_HI_WIDTH 32
/* MMIO region 2 base address (bus address), 0 if unused */
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_BASE_OFST 48
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_BASE_LEN 8
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_BASE_LO_OFST 48
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_BASE_LO_LEN 4
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_BASE_LO_LBN 384
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_BASE_LO_WIDTH 32
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_BASE_HI_OFST 52
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_BASE_HI_LEN 4
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_BASE_HI_LBN 416
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_BASE_HI_WIDTH 32
/* MMIO region 2 size, 0 if unused */
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_SIZE_OFST 56
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_SIZE_LEN 8
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_SIZE_LO_OFST 56
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_SIZE_LO_LEN 4
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_SIZE_LO_LBN 448
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_SIZE_LO_WIDTH 32
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_SIZE_HI_OFST 60
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_SIZE_HI_LEN 4
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_SIZE_HI_LBN 480
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_SIZE_HI_WIDTH 32
/* MMIO region 3 base address (bus address), 0 if unused */
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_BASE_OFST 64
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_BASE_LEN 8
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_BASE_LO_OFST 64
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_BASE_LO_LEN 4
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_BASE_LO_LBN 512
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_BASE_LO_WIDTH 32
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_BASE_HI_OFST 68
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_BASE_HI_LEN 4
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_BASE_HI_LBN 544
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_BASE_HI_WIDTH 32
/* MMIO region 3 size, 0 if unused */
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_SIZE_OFST 72
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_SIZE_LEN 8
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_SIZE_LO_OFST 72
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_SIZE_LO_LEN 4
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_SIZE_LO_LBN 576
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_SIZE_LO_WIDTH 32
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_SIZE_HI_OFST 76
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_SIZE_HI_LEN 4
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_SIZE_HI_LBN 608
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_SIZE_HI_WIDTH 32
/* MSI vector count */
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MSI_COUNT_OFST 80
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_MSI_COUNT_LEN 4
/* Requester ID used by device (SMMU StreamID, GIC ITS DeviceID) */
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_REQUESTER_ID_OFST 84
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_REQUESTER_ID_LEN 4
/***********************************/
/*
* MC_CMD_CDX_DEVICE_RESET
* After this call completes, device DMA and interrupts are quiesced, devices
* logic is reset in a hardware-specific way and DMA bus mastering is disabled.
*/
#define MC_CMD_CDX_DEVICE_RESET 0x6
#define MC_CMD_CDX_DEVICE_RESET_MSGSET 0x6
#undef MC_CMD_0x6_PRIVILEGE_CTG
#define MC_CMD_0x6_PRIVILEGE_CTG SRIOV_CTG_ADMIN
/* MC_CMD_CDX_DEVICE_RESET_IN msgrequest */
#define MC_CMD_CDX_DEVICE_RESET_IN_LEN 8
/* Device bus number, in range 0 to BUS_COUNT-1 */
#define MC_CMD_CDX_DEVICE_RESET_IN_BUS_OFST 0
#define MC_CMD_CDX_DEVICE_RESET_IN_BUS_LEN 4
/* Device number relative to the bus, in range 0 to DEVICE_COUNT-1 for that bus */
#define MC_CMD_CDX_DEVICE_RESET_IN_DEVICE_OFST 4
#define MC_CMD_CDX_DEVICE_RESET_IN_DEVICE_LEN 4
/*
* MC_CMD_CDX_DEVICE_RESET_OUT msgresponse: The device is quiesced and all
* pending device initiated DMA has completed.
*/
#define MC_CMD_CDX_DEVICE_RESET_OUT_LEN 0
/***********************************/
/*
* MC_CMD_CDX_DEVICE_CONTROL_SET
* If BUS_MASTER is set to disabled, device DMA and interrupts are quiesced.
* Pending DMA requests and MSI interrupts are flushed and no further DMA or
* interrupts are issued after this command returns. If BUS_MASTER is set to
* enabled, device is allowed to initiate DMA. Whether interrupts are enabled
* also depends on the value of MSI_ENABLE bit. Note that, in this case, the
* device may start DMA before the host receives and processes the MCDI
* response. MSI_ENABLE masks or unmasks device interrupts only. Note that for
* interrupts to be delivered to the host, both BUS_MASTER and MSI_ENABLE needs
* to be set. MMIO_REGIONS_ENABLE enables or disables host accesses to device
* MMIO regions. Note that an implementation is allowed to permanently set this
* bit to 1, in which case MC_CMD_CDX_DEVICE_CONTROL_GET will always return 1
* for this bit, regardless of the value set here.
*/
#define MC_CMD_CDX_DEVICE_CONTROL_SET 0x7
#define MC_CMD_CDX_DEVICE_CONTROL_SET_MSGSET 0x7
#undef MC_CMD_0x7_PRIVILEGE_CTG
#define MC_CMD_0x7_PRIVILEGE_CTG SRIOV_CTG_ADMIN
/* MC_CMD_CDX_DEVICE_CONTROL_SET_IN msgrequest */
#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_LEN 12
/* Device bus number, in range 0 to BUS_COUNT-1 */
#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_BUS_OFST 0
#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_BUS_LEN 4
/* Device number relative to the bus, in range 0 to DEVICE_COUNT-1 for that bus */
#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_DEVICE_OFST 4
#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_DEVICE_LEN 4
#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_FLAGS_OFST 8
#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_FLAGS_LEN 4
#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_BUS_MASTER_ENABLE_OFST 8
#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_BUS_MASTER_ENABLE_LBN 0
#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_BUS_MASTER_ENABLE_WIDTH 1
#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_MSI_ENABLE_OFST 8
#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_MSI_ENABLE_LBN 1
#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_MSI_ENABLE_WIDTH 1
#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_MMIO_REGIONS_ENABLE_OFST 8
#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_MMIO_REGIONS_ENABLE_LBN 2
#define MC_CMD_CDX_DEVICE_CONTROL_SET_IN_MMIO_REGIONS_ENABLE_WIDTH 1
/* MC_CMD_CDX_DEVICE_CONTROL_SET_OUT msgresponse */
#define MC_CMD_CDX_DEVICE_CONTROL_SET_OUT_LEN 0
/***********************************/
/*
* MC_CMD_CDX_DEVICE_CONTROL_GET
* Returns device DMA, interrupt and MMIO region access control bits. See
* MC_CMD_CDX_DEVICE_CONTROL_SET for definition of the available control bits.
*/
#define MC_CMD_CDX_DEVICE_CONTROL_GET 0x8
#define MC_CMD_CDX_DEVICE_CONTROL_GET_MSGSET 0x8
#undef MC_CMD_0x8_PRIVILEGE_CTG
#define MC_CMD_0x8_PRIVILEGE_CTG SRIOV_CTG_ADMIN
/* MC_CMD_CDX_DEVICE_CONTROL_GET_IN msgrequest */
#define MC_CMD_CDX_DEVICE_CONTROL_GET_IN_LEN 8
/* Device bus number, in range 0 to BUS_COUNT-1 */
#define MC_CMD_CDX_DEVICE_CONTROL_GET_IN_BUS_OFST 0
#define MC_CMD_CDX_DEVICE_CONTROL_GET_IN_BUS_LEN 4
/* Device number relative to the bus, in range 0 to DEVICE_COUNT-1 for that bus */
#define MC_CMD_CDX_DEVICE_CONTROL_GET_IN_DEVICE_OFST 4
#define MC_CMD_CDX_DEVICE_CONTROL_GET_IN_DEVICE_LEN 4
/* MC_CMD_CDX_DEVICE_CONTROL_GET_OUT msgresponse */
#define MC_CMD_CDX_DEVICE_CONTROL_GET_OUT_LEN 4
#define MC_CMD_CDX_DEVICE_CONTROL_GET_OUT_FLAGS_OFST 0
#define MC_CMD_CDX_DEVICE_CONTROL_GET_OUT_FLAGS_LEN 4
#define MC_CMD_CDX_DEVICE_CONTROL_GET_OUT_BUS_MASTER_ENABLE_OFST 0
#define MC_CMD_CDX_DEVICE_CONTROL_GET_OUT_BUS_MASTER_ENABLE_LBN 0
#define MC_CMD_CDX_DEVICE_CONTROL_GET_OUT_BUS_MASTER_ENABLE_WIDTH 1
#define MC_CMD_CDX_DEVICE_CONTROL_GET_OUT_MSI_ENABLE_OFST 0
#define MC_CMD_CDX_DEVICE_CONTROL_GET_OUT_MSI_ENABLE_LBN 1
#define MC_CMD_CDX_DEVICE_CONTROL_GET_OUT_MSI_ENABLE_WIDTH 1
#define MC_CMD_CDX_DEVICE_CONTROL_GET_OUT_MMIO_REGIONS_ENABLE_OFST 0
#define MC_CMD_CDX_DEVICE_CONTROL_GET_OUT_MMIO_REGIONS_ENABLE_LBN 2
#define MC_CMD_CDX_DEVICE_CONTROL_GET_OUT_MMIO_REGIONS_ENABLE_WIDTH 1
/***********************************/
/* MC_CMD_V2_EXTN - Encapsulation for a v2 extended command */
#define MC_CMD_V2_EXTN 0x7f
/* MC_CMD_V2_EXTN_IN msgrequest */
#define MC_CMD_V2_EXTN_IN_LEN 4
/* the extended command number */
#define MC_CMD_V2_EXTN_IN_EXTENDED_CMD_LBN 0
#define MC_CMD_V2_EXTN_IN_EXTENDED_CMD_WIDTH 15
#define MC_CMD_V2_EXTN_IN_UNUSED_LBN 15
#define MC_CMD_V2_EXTN_IN_UNUSED_WIDTH 1
/* the actual length of the encapsulated command */
#define MC_CMD_V2_EXTN_IN_ACTUAL_LEN_LBN 16
#define MC_CMD_V2_EXTN_IN_ACTUAL_LEN_WIDTH 10
#define MC_CMD_V2_EXTN_IN_UNUSED2_LBN 26
#define MC_CMD_V2_EXTN_IN_UNUSED2_WIDTH 2
/* Type of command/response */
#define MC_CMD_V2_EXTN_IN_MESSAGE_TYPE_LBN 28
#define MC_CMD_V2_EXTN_IN_MESSAGE_TYPE_WIDTH 4
/*
* enum: MCDI command directed to versal-net. MCDI responses of this type
* are not defined.
*/
#define MC_CMD_V2_EXTN_IN_MCDI_MESSAGE_TYPE_PLATFORM 0x2
#endif /* MC_CDX_PCOL_H */

View File

@ -0,0 +1,903 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Management-Controller-to-Driver Interface
*
* Copyright 2008-2013 Solarflare Communications Inc.
* Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
*/
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/spinlock.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/if_vlan.h>
#include <linux/timer.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <linux/device.h>
#include <linux/rwsem.h>
#include <linux/vmalloc.h>
#include <net/netevent.h>
#include <linux/log2.h>
#include <linux/net_tstamp.h>
#include <linux/wait.h>
#include "bitfield.h"
#include "mcdi.h"
struct cdx_mcdi_copy_buffer {
struct cdx_dword buffer[DIV_ROUND_UP(MCDI_CTL_SDU_LEN_MAX, 4)];
};
#ifdef CONFIG_MCDI_LOGGING
#define LOG_LINE_MAX (1024 - 32)
#endif
static void cdx_mcdi_cancel_cmd(struct cdx_mcdi *cdx, struct cdx_mcdi_cmd *cmd);
static void cdx_mcdi_wait_for_cleanup(struct cdx_mcdi *cdx);
static int cdx_mcdi_rpc_async_internal(struct cdx_mcdi *cdx,
struct cdx_mcdi_cmd *cmd,
unsigned int *handle);
static void cdx_mcdi_start_or_queue(struct cdx_mcdi_iface *mcdi,
bool allow_retry);
static void cdx_mcdi_cmd_start_or_queue(struct cdx_mcdi_iface *mcdi,
struct cdx_mcdi_cmd *cmd);
static bool cdx_mcdi_complete_cmd(struct cdx_mcdi_iface *mcdi,
struct cdx_mcdi_cmd *cmd,
struct cdx_dword *outbuf,
int len,
struct list_head *cleanup_list);
static void cdx_mcdi_timeout_cmd(struct cdx_mcdi_iface *mcdi,
struct cdx_mcdi_cmd *cmd,
struct list_head *cleanup_list);
static void cdx_mcdi_cmd_work(struct work_struct *context);
static void cdx_mcdi_mode_fail(struct cdx_mcdi *cdx, struct list_head *cleanup_list);
static void _cdx_mcdi_display_error(struct cdx_mcdi *cdx, unsigned int cmd,
size_t inlen, int raw, int arg, int err_no);
static bool cdx_cmd_cancelled(struct cdx_mcdi_cmd *cmd)
{
return cmd->state == MCDI_STATE_RUNNING_CANCELLED;
}
static void cdx_mcdi_cmd_release(struct kref *ref)
{
kfree(container_of(ref, struct cdx_mcdi_cmd, ref));
}
static unsigned int cdx_mcdi_cmd_handle(struct cdx_mcdi_cmd *cmd)
{
return cmd->handle;
}
static void _cdx_mcdi_remove_cmd(struct cdx_mcdi_iface *mcdi,
struct cdx_mcdi_cmd *cmd,
struct list_head *cleanup_list)
{
/* if cancelled, the completers have already been called */
if (cdx_cmd_cancelled(cmd))
return;
if (cmd->completer) {
list_add_tail(&cmd->cleanup_list, cleanup_list);
++mcdi->outstanding_cleanups;
kref_get(&cmd->ref);
}
}
static void cdx_mcdi_remove_cmd(struct cdx_mcdi_iface *mcdi,
struct cdx_mcdi_cmd *cmd,
struct list_head *cleanup_list)
{
list_del(&cmd->list);
_cdx_mcdi_remove_cmd(mcdi, cmd, cleanup_list);
cmd->state = MCDI_STATE_FINISHED;
kref_put(&cmd->ref, cdx_mcdi_cmd_release);
if (list_empty(&mcdi->cmd_list))
wake_up(&mcdi->cmd_complete_wq);
}
static unsigned long cdx_mcdi_rpc_timeout(struct cdx_mcdi *cdx, unsigned int cmd)
{
if (!cdx->mcdi_ops->mcdi_rpc_timeout)
return MCDI_RPC_TIMEOUT;
else
return cdx->mcdi_ops->mcdi_rpc_timeout(cdx, cmd);
}
int cdx_mcdi_init(struct cdx_mcdi *cdx)
{
struct cdx_mcdi_iface *mcdi;
int rc = -ENOMEM;
cdx->mcdi = kzalloc(sizeof(*cdx->mcdi), GFP_KERNEL);
if (!cdx->mcdi)
goto fail;
mcdi = cdx_mcdi_if(cdx);
mcdi->cdx = cdx;
#ifdef CONFIG_MCDI_LOGGING
mcdi->logging_buffer = kmalloc(LOG_LINE_MAX, GFP_KERNEL);
if (!mcdi->logging_buffer)
goto fail2;
#endif
mcdi->workqueue = alloc_ordered_workqueue("mcdi_wq", 0);
if (!mcdi->workqueue)
goto fail3;
mutex_init(&mcdi->iface_lock);
mcdi->mode = MCDI_MODE_EVENTS;
INIT_LIST_HEAD(&mcdi->cmd_list);
init_waitqueue_head(&mcdi->cmd_complete_wq);
mcdi->new_epoch = true;
return 0;
fail3:
#ifdef CONFIG_MCDI_LOGGING
kfree(mcdi->logging_buffer);
fail2:
#endif
kfree(cdx->mcdi);
cdx->mcdi = NULL;
fail:
return rc;
}
void cdx_mcdi_finish(struct cdx_mcdi *cdx)
{
struct cdx_mcdi_iface *mcdi;
mcdi = cdx_mcdi_if(cdx);
if (!mcdi)
return;
cdx_mcdi_wait_for_cleanup(cdx);
#ifdef CONFIG_MCDI_LOGGING
kfree(mcdi->logging_buffer);
#endif
destroy_workqueue(mcdi->workqueue);
kfree(cdx->mcdi);
cdx->mcdi = NULL;
}
static bool cdx_mcdi_flushed(struct cdx_mcdi_iface *mcdi, bool ignore_cleanups)
{
bool flushed;
mutex_lock(&mcdi->iface_lock);
flushed = list_empty(&mcdi->cmd_list) &&
(ignore_cleanups || !mcdi->outstanding_cleanups);
mutex_unlock(&mcdi->iface_lock);
return flushed;
}
/* Wait for outstanding MCDI commands to complete. */
static void cdx_mcdi_wait_for_cleanup(struct cdx_mcdi *cdx)
{
struct cdx_mcdi_iface *mcdi = cdx_mcdi_if(cdx);
if (!mcdi)
return;
wait_event(mcdi->cmd_complete_wq,
cdx_mcdi_flushed(mcdi, false));
}
int cdx_mcdi_wait_for_quiescence(struct cdx_mcdi *cdx,
unsigned int timeout_jiffies)
{
struct cdx_mcdi_iface *mcdi = cdx_mcdi_if(cdx);
DEFINE_WAIT_FUNC(wait, woken_wake_function);
int rc = 0;
if (!mcdi)
return -EINVAL;
flush_workqueue(mcdi->workqueue);
add_wait_queue(&mcdi->cmd_complete_wq, &wait);
while (!cdx_mcdi_flushed(mcdi, true)) {
rc = wait_woken(&wait, TASK_IDLE, timeout_jiffies);
if (rc)
continue;
break;
}
remove_wait_queue(&mcdi->cmd_complete_wq, &wait);
if (rc > 0)
rc = 0;
else if (rc == 0)
rc = -ETIMEDOUT;
return rc;
}
static u8 cdx_mcdi_payload_csum(const struct cdx_dword *hdr, size_t hdr_len,
const struct cdx_dword *sdu, size_t sdu_len)
{
u8 *p = (u8 *)hdr;
u8 csum = 0;
int i;
for (i = 0; i < hdr_len; i++)
csum += p[i];
p = (u8 *)sdu;
for (i = 0; i < sdu_len; i++)
csum += p[i];
return ~csum & 0xff;
}
static void cdx_mcdi_send_request(struct cdx_mcdi *cdx,
struct cdx_mcdi_cmd *cmd)
{
struct cdx_mcdi_iface *mcdi = cdx_mcdi_if(cdx);
const struct cdx_dword *inbuf = cmd->inbuf;
size_t inlen = cmd->inlen;
struct cdx_dword hdr[2];
size_t hdr_len;
bool not_epoch;
u32 xflags;
#ifdef CONFIG_MCDI_LOGGING
char *buf;
#endif
if (!mcdi)
return;
#ifdef CONFIG_MCDI_LOGGING
buf = mcdi->logging_buffer; /* page-sized */
#endif
mcdi->prev_seq = cmd->seq;
mcdi->seq_held_by[cmd->seq] = cmd;
mcdi->db_held_by = cmd;
cmd->started = jiffies;
not_epoch = !mcdi->new_epoch;
xflags = 0;
/* MCDI v2 */
WARN_ON(inlen > MCDI_CTL_SDU_LEN_MAX_V2);
CDX_POPULATE_DWORD_7(hdr[0],
MCDI_HEADER_RESPONSE, 0,
MCDI_HEADER_RESYNC, 1,
MCDI_HEADER_CODE, MC_CMD_V2_EXTN,
MCDI_HEADER_DATALEN, 0,
MCDI_HEADER_SEQ, cmd->seq,
MCDI_HEADER_XFLAGS, xflags,
MCDI_HEADER_NOT_EPOCH, not_epoch);
CDX_POPULATE_DWORD_3(hdr[1],
MC_CMD_V2_EXTN_IN_EXTENDED_CMD, cmd->cmd,
MC_CMD_V2_EXTN_IN_ACTUAL_LEN, inlen,
MC_CMD_V2_EXTN_IN_MESSAGE_TYPE,
MC_CMD_V2_EXTN_IN_MCDI_MESSAGE_TYPE_PLATFORM);
hdr_len = 8;
#ifdef CONFIG_MCDI_LOGGING
if (!WARN_ON_ONCE(!buf)) {
const struct cdx_dword *frags[] = { hdr, inbuf };
const size_t frag_len[] = { hdr_len, round_up(inlen, 4) };
int bytes = 0;
int i, j;
for (j = 0; j < ARRAY_SIZE(frags); j++) {
const struct cdx_dword *frag;
frag = frags[j];
for (i = 0;
i < frag_len[j] / 4;
i++) {
/*
* Do not exceed the internal printk limit.
* The string before that is just over 70 bytes.
*/
if ((bytes + 75) > LOG_LINE_MAX) {
pr_info("MCDI RPC REQ:%s \\\n", buf);
bytes = 0;
}
bytes += snprintf(buf + bytes,
LOG_LINE_MAX - bytes, " %08x",
le32_to_cpu(frag[i].cdx_u32));
}
}
pr_info("MCDI RPC REQ:%s\n", buf);
}
#endif
hdr[0].cdx_u32 |= (__force __le32)(cdx_mcdi_payload_csum(hdr, hdr_len, inbuf, inlen) <<
MCDI_HEADER_XFLAGS_LBN);
cdx->mcdi_ops->mcdi_request(cdx, hdr, hdr_len, inbuf, inlen);
mcdi->new_epoch = false;
}
static int cdx_mcdi_errno(struct cdx_mcdi *cdx, unsigned int mcdi_err)
{
switch (mcdi_err) {
case 0:
case MC_CMD_ERR_QUEUE_FULL:
return mcdi_err;
case MC_CMD_ERR_EPERM:
return -EPERM;
case MC_CMD_ERR_ENOENT:
return -ENOENT;
case MC_CMD_ERR_EINTR:
return -EINTR;
case MC_CMD_ERR_EAGAIN:
return -EAGAIN;
case MC_CMD_ERR_EACCES:
return -EACCES;
case MC_CMD_ERR_EBUSY:
return -EBUSY;
case MC_CMD_ERR_EINVAL:
return -EINVAL;
case MC_CMD_ERR_ERANGE:
return -ERANGE;
case MC_CMD_ERR_EDEADLK:
return -EDEADLK;
case MC_CMD_ERR_ENOSYS:
return -EOPNOTSUPP;
case MC_CMD_ERR_ETIME:
return -ETIME;
case MC_CMD_ERR_EALREADY:
return -EALREADY;
case MC_CMD_ERR_ENOSPC:
return -ENOSPC;
case MC_CMD_ERR_ENOMEM:
return -ENOMEM;
case MC_CMD_ERR_ENOTSUP:
return -EOPNOTSUPP;
case MC_CMD_ERR_ALLOC_FAIL:
return -ENOBUFS;
case MC_CMD_ERR_MAC_EXIST:
return -EADDRINUSE;
case MC_CMD_ERR_NO_EVB_PORT:
return -EAGAIN;
default:
return -EPROTO;
}
}
static void cdx_mcdi_process_cleanup_list(struct cdx_mcdi *cdx,
struct list_head *cleanup_list)
{
struct cdx_mcdi_iface *mcdi = cdx_mcdi_if(cdx);
unsigned int cleanups = 0;
if (!mcdi)
return;
while (!list_empty(cleanup_list)) {
struct cdx_mcdi_cmd *cmd =
list_first_entry(cleanup_list,
struct cdx_mcdi_cmd, cleanup_list);
cmd->completer(cdx, cmd->cookie, cmd->rc,
cmd->outbuf, cmd->outlen);
list_del(&cmd->cleanup_list);
kref_put(&cmd->ref, cdx_mcdi_cmd_release);
++cleanups;
}
if (cleanups) {
bool all_done;
mutex_lock(&mcdi->iface_lock);
CDX_WARN_ON_PARANOID(cleanups > mcdi->outstanding_cleanups);
all_done = (mcdi->outstanding_cleanups -= cleanups) == 0;
mutex_unlock(&mcdi->iface_lock);
if (all_done)
wake_up(&mcdi->cmd_complete_wq);
}
}
static void _cdx_mcdi_cancel_cmd(struct cdx_mcdi_iface *mcdi,
unsigned int handle,
struct list_head *cleanup_list)
{
struct cdx_mcdi_cmd *cmd;
list_for_each_entry(cmd, &mcdi->cmd_list, list)
if (cdx_mcdi_cmd_handle(cmd) == handle) {
switch (cmd->state) {
case MCDI_STATE_QUEUED:
case MCDI_STATE_RETRY:
pr_debug("command %#x inlen %zu cancelled in queue\n",
cmd->cmd, cmd->inlen);
/* if not yet running, properly cancel it */
cmd->rc = -EPIPE;
cdx_mcdi_remove_cmd(mcdi, cmd, cleanup_list);
break;
case MCDI_STATE_RUNNING:
case MCDI_STATE_RUNNING_CANCELLED:
case MCDI_STATE_FINISHED:
default:
/* invalid state? */
WARN_ON(1);
}
break;
}
}
static void cdx_mcdi_cancel_cmd(struct cdx_mcdi *cdx, struct cdx_mcdi_cmd *cmd)
{
struct cdx_mcdi_iface *mcdi = cdx_mcdi_if(cdx);
LIST_HEAD(cleanup_list);
if (!mcdi)
return;
mutex_lock(&mcdi->iface_lock);
cdx_mcdi_timeout_cmd(mcdi, cmd, &cleanup_list);
mutex_unlock(&mcdi->iface_lock);
cdx_mcdi_process_cleanup_list(cdx, &cleanup_list);
}
struct cdx_mcdi_blocking_data {
struct kref ref;
bool done;
wait_queue_head_t wq;
int rc;
struct cdx_dword *outbuf;
size_t outlen;
size_t outlen_actual;
};
static void cdx_mcdi_blocking_data_release(struct kref *ref)
{
kfree(container_of(ref, struct cdx_mcdi_blocking_data, ref));
}
static void cdx_mcdi_rpc_completer(struct cdx_mcdi *cdx, unsigned long cookie,
int rc, struct cdx_dword *outbuf,
size_t outlen_actual)
{
struct cdx_mcdi_blocking_data *wait_data =
(struct cdx_mcdi_blocking_data *)cookie;
wait_data->rc = rc;
memcpy(wait_data->outbuf, outbuf,
min(outlen_actual, wait_data->outlen));
wait_data->outlen_actual = outlen_actual;
/* memory barrier */
smp_wmb();
wait_data->done = true;
wake_up(&wait_data->wq);
kref_put(&wait_data->ref, cdx_mcdi_blocking_data_release);
}
static int cdx_mcdi_rpc_sync(struct cdx_mcdi *cdx, unsigned int cmd,
const struct cdx_dword *inbuf, size_t inlen,
struct cdx_dword *outbuf, size_t outlen,
size_t *outlen_actual, bool quiet)
{
struct cdx_mcdi_blocking_data *wait_data;
struct cdx_mcdi_cmd *cmd_item;
unsigned int handle;
int rc;
if (outlen_actual)
*outlen_actual = 0;
wait_data = kmalloc(sizeof(*wait_data), GFP_KERNEL);
if (!wait_data)
return -ENOMEM;
cmd_item = kmalloc(sizeof(*cmd_item), GFP_KERNEL);
if (!cmd_item) {
kfree(wait_data);
return -ENOMEM;
}
kref_init(&wait_data->ref);
wait_data->done = false;
init_waitqueue_head(&wait_data->wq);
wait_data->outbuf = outbuf;
wait_data->outlen = outlen;
kref_init(&cmd_item->ref);
cmd_item->quiet = quiet;
cmd_item->cookie = (unsigned long)wait_data;
cmd_item->completer = &cdx_mcdi_rpc_completer;
cmd_item->cmd = cmd;
cmd_item->inlen = inlen;
cmd_item->inbuf = inbuf;
/* Claim an extra reference for the completer to put. */
kref_get(&wait_data->ref);
rc = cdx_mcdi_rpc_async_internal(cdx, cmd_item, &handle);
if (rc) {
kref_put(&wait_data->ref, cdx_mcdi_blocking_data_release);
goto out;
}
if (!wait_event_timeout(wait_data->wq, wait_data->done,
cdx_mcdi_rpc_timeout(cdx, cmd)) &&
!wait_data->done) {
pr_err("MC command 0x%x inlen %zu timed out (sync)\n",
cmd, inlen);
cdx_mcdi_cancel_cmd(cdx, cmd_item);
wait_data->rc = -ETIMEDOUT;
wait_data->outlen_actual = 0;
}
if (outlen_actual)
*outlen_actual = wait_data->outlen_actual;
rc = wait_data->rc;
out:
kref_put(&wait_data->ref, cdx_mcdi_blocking_data_release);
return rc;
}
static bool cdx_mcdi_get_seq(struct cdx_mcdi_iface *mcdi, unsigned char *seq)
{
*seq = mcdi->prev_seq;
do {
*seq = (*seq + 1) % ARRAY_SIZE(mcdi->seq_held_by);
} while (mcdi->seq_held_by[*seq] && *seq != mcdi->prev_seq);
return !mcdi->seq_held_by[*seq];
}
static int cdx_mcdi_rpc_async_internal(struct cdx_mcdi *cdx,
struct cdx_mcdi_cmd *cmd,
unsigned int *handle)
{
struct cdx_mcdi_iface *mcdi = cdx_mcdi_if(cdx);
LIST_HEAD(cleanup_list);
if (!mcdi) {
kref_put(&cmd->ref, cdx_mcdi_cmd_release);
return -ENETDOWN;
}
if (mcdi->mode == MCDI_MODE_FAIL) {
kref_put(&cmd->ref, cdx_mcdi_cmd_release);
return -ENETDOWN;
}
cmd->mcdi = mcdi;
INIT_WORK(&cmd->work, cdx_mcdi_cmd_work);
INIT_LIST_HEAD(&cmd->list);
INIT_LIST_HEAD(&cmd->cleanup_list);
cmd->rc = 0;
cmd->outbuf = NULL;
cmd->outlen = 0;
queue_work(mcdi->workqueue, &cmd->work);
return 0;
}
static void cdx_mcdi_cmd_start_or_queue(struct cdx_mcdi_iface *mcdi,
struct cdx_mcdi_cmd *cmd)
{
struct cdx_mcdi *cdx = mcdi->cdx;
u8 seq;
if (!mcdi->db_held_by &&
cdx_mcdi_get_seq(mcdi, &seq)) {
cmd->seq = seq;
cmd->reboot_seen = false;
cdx_mcdi_send_request(cdx, cmd);
cmd->state = MCDI_STATE_RUNNING;
} else {
cmd->state = MCDI_STATE_QUEUED;
}
}
/* try to advance other commands */
static void cdx_mcdi_start_or_queue(struct cdx_mcdi_iface *mcdi,
bool allow_retry)
{
struct cdx_mcdi_cmd *cmd, *tmp;
list_for_each_entry_safe(cmd, tmp, &mcdi->cmd_list, list)
if (cmd->state == MCDI_STATE_QUEUED ||
(cmd->state == MCDI_STATE_RETRY && allow_retry))
cdx_mcdi_cmd_start_or_queue(mcdi, cmd);
}
void cdx_mcdi_process_cmd(struct cdx_mcdi *cdx, struct cdx_dword *outbuf, int len)
{
struct cdx_mcdi_iface *mcdi;
struct cdx_mcdi_cmd *cmd;
LIST_HEAD(cleanup_list);
unsigned int respseq;
if (!len || !outbuf) {
pr_err("Got empty MC response\n");
return;
}
mcdi = cdx_mcdi_if(cdx);
if (!mcdi)
return;
respseq = CDX_DWORD_FIELD(outbuf[0], MCDI_HEADER_SEQ);
mutex_lock(&mcdi->iface_lock);
cmd = mcdi->seq_held_by[respseq];
if (cmd) {
if (cmd->state == MCDI_STATE_FINISHED) {
mutex_unlock(&mcdi->iface_lock);
kref_put(&cmd->ref, cdx_mcdi_cmd_release);
return;
}
cdx_mcdi_complete_cmd(mcdi, cmd, outbuf, len, &cleanup_list);
} else {
pr_err("MC response unexpected for seq : %0X\n", respseq);
}
mutex_unlock(&mcdi->iface_lock);
cdx_mcdi_process_cleanup_list(mcdi->cdx, &cleanup_list);
}
static void cdx_mcdi_cmd_work(struct work_struct *context)
{
struct cdx_mcdi_cmd *cmd =
container_of(context, struct cdx_mcdi_cmd, work);
struct cdx_mcdi_iface *mcdi = cmd->mcdi;
mutex_lock(&mcdi->iface_lock);
cmd->handle = mcdi->prev_handle++;
list_add_tail(&cmd->list, &mcdi->cmd_list);
cdx_mcdi_cmd_start_or_queue(mcdi, cmd);
mutex_unlock(&mcdi->iface_lock);
}
/*
* Returns true if the MCDI module is finished with the command.
* (examples of false would be if the command was proxied, or it was
* rejected by the MC due to lack of resources and requeued).
*/
static bool cdx_mcdi_complete_cmd(struct cdx_mcdi_iface *mcdi,
struct cdx_mcdi_cmd *cmd,
struct cdx_dword *outbuf,
int len,
struct list_head *cleanup_list)
{
size_t resp_hdr_len, resp_data_len;
struct cdx_mcdi *cdx = mcdi->cdx;
unsigned int respcmd, error;
bool completed = false;
int rc;
/* ensure the command can't go away before this function returns */
kref_get(&cmd->ref);
respcmd = CDX_DWORD_FIELD(outbuf[0], MCDI_HEADER_CODE);
error = CDX_DWORD_FIELD(outbuf[0], MCDI_HEADER_ERROR);
if (respcmd != MC_CMD_V2_EXTN) {
resp_hdr_len = 4;
resp_data_len = CDX_DWORD_FIELD(outbuf[0], MCDI_HEADER_DATALEN);
} else {
resp_data_len = 0;
resp_hdr_len = 8;
if (len >= 8)
resp_data_len =
CDX_DWORD_FIELD(outbuf[1], MC_CMD_V2_EXTN_IN_ACTUAL_LEN);
}
if ((resp_hdr_len + resp_data_len) > len) {
pr_warn("Incomplete MCDI response received %d. Expected %zu\n",
len, (resp_hdr_len + resp_data_len));
resp_data_len = 0;
}
#ifdef CONFIG_MCDI_LOGGING
if (!WARN_ON_ONCE(!mcdi->logging_buffer)) {
char *log = mcdi->logging_buffer;
int i, bytes = 0;
size_t rlen;
WARN_ON_ONCE(resp_hdr_len % 4);
rlen = resp_hdr_len / 4 + DIV_ROUND_UP(resp_data_len, 4);
for (i = 0; i < rlen; i++) {
if ((bytes + 75) > LOG_LINE_MAX) {
pr_info("MCDI RPC RESP:%s \\\n", log);
bytes = 0;
}
bytes += snprintf(log + bytes, LOG_LINE_MAX - bytes,
" %08x", le32_to_cpu(outbuf[i].cdx_u32));
}
pr_info("MCDI RPC RESP:%s\n", log);
}
#endif
if (error && resp_data_len == 0) {
/* MC rebooted during command */
rc = -EIO;
} else {
if (WARN_ON_ONCE(error && resp_data_len < 4))
resp_data_len = 4;
if (error) {
rc = CDX_DWORD_FIELD(outbuf[resp_hdr_len / 4], CDX_DWORD);
if (!cmd->quiet) {
int err_arg = 0;
if (resp_data_len >= MC_CMD_ERR_ARG_OFST + 4) {
int offset = (resp_hdr_len + MC_CMD_ERR_ARG_OFST) / 4;
err_arg = CDX_DWORD_VAL(outbuf[offset]);
}
_cdx_mcdi_display_error(cdx, cmd->cmd,
cmd->inlen, rc, err_arg,
cdx_mcdi_errno(cdx, rc));
}
rc = cdx_mcdi_errno(cdx, rc);
} else {
rc = 0;
}
}
/* free doorbell */
if (mcdi->db_held_by == cmd)
mcdi->db_held_by = NULL;
if (cdx_cmd_cancelled(cmd)) {
list_del(&cmd->list);
kref_put(&cmd->ref, cdx_mcdi_cmd_release);
completed = true;
} else if (rc == MC_CMD_ERR_QUEUE_FULL) {
cmd->state = MCDI_STATE_RETRY;
} else {
cmd->rc = rc;
cmd->outbuf = outbuf + DIV_ROUND_UP(resp_hdr_len, 4);
cmd->outlen = resp_data_len;
cdx_mcdi_remove_cmd(mcdi, cmd, cleanup_list);
completed = true;
}
/* free sequence number and buffer */
mcdi->seq_held_by[cmd->seq] = NULL;
cdx_mcdi_start_or_queue(mcdi, rc != MC_CMD_ERR_QUEUE_FULL);
/* wake up anyone waiting for flush */
wake_up(&mcdi->cmd_complete_wq);
kref_put(&cmd->ref, cdx_mcdi_cmd_release);
return completed;
}
static void cdx_mcdi_timeout_cmd(struct cdx_mcdi_iface *mcdi,
struct cdx_mcdi_cmd *cmd,
struct list_head *cleanup_list)
{
struct cdx_mcdi *cdx = mcdi->cdx;
pr_err("MC command 0x%x inlen %zu state %d timed out after %u ms\n",
cmd->cmd, cmd->inlen, cmd->state,
jiffies_to_msecs(jiffies - cmd->started));
cmd->rc = -ETIMEDOUT;
cdx_mcdi_remove_cmd(mcdi, cmd, cleanup_list);
cdx_mcdi_mode_fail(cdx, cleanup_list);
}
/**
* cdx_mcdi_rpc - Issue an MCDI command and wait for completion
* @cdx: NIC through which to issue the command
* @cmd: Command type number
* @inbuf: Command parameters
* @inlen: Length of command parameters, in bytes. Must be a multiple
* of 4 and no greater than %MCDI_CTL_SDU_LEN_MAX_V1.
* @outbuf: Response buffer. May be %NULL if @outlen is 0.
* @outlen: Length of response buffer, in bytes. If the actual
* response is longer than @outlen & ~3, it will be truncated
* to that length.
* @outlen_actual: Pointer through which to return the actual response
* length. May be %NULL if this is not needed.
*
* This function may sleep and therefore must be called in process
* context.
*
* Return: A negative error code, or zero if successful. The error
* code may come from the MCDI response or may indicate a failure
* to communicate with the MC. In the former case, the response
* will still be copied to @outbuf and *@outlen_actual will be
* set accordingly. In the latter case, *@outlen_actual will be
* set to zero.
*/
int cdx_mcdi_rpc(struct cdx_mcdi *cdx, unsigned int cmd,
const struct cdx_dword *inbuf, size_t inlen,
struct cdx_dword *outbuf, size_t outlen,
size_t *outlen_actual)
{
return cdx_mcdi_rpc_sync(cdx, cmd, inbuf, inlen, outbuf, outlen,
outlen_actual, false);
}
/**
* cdx_mcdi_rpc_async - Schedule an MCDI command to run asynchronously
* @cdx: NIC through which to issue the command
* @cmd: Command type number
* @inbuf: Command parameters
* @inlen: Length of command parameters, in bytes
* @complete: Function to be called on completion or cancellation.
* @cookie: Arbitrary value to be passed to @complete.
*
* This function does not sleep and therefore may be called in atomic
* context. It will fail if event queues are disabled or if MCDI
* event completions have been disabled due to an error.
*
* If it succeeds, the @complete function will be called exactly once
* in process context, when one of the following occurs:
* (a) the completion event is received (in process context)
* (b) event queues are disabled (in the process that disables them)
*/
int
cdx_mcdi_rpc_async(struct cdx_mcdi *cdx, unsigned int cmd,
const struct cdx_dword *inbuf, size_t inlen,
cdx_mcdi_async_completer *complete, unsigned long cookie)
{
struct cdx_mcdi_cmd *cmd_item =
kmalloc(sizeof(struct cdx_mcdi_cmd) + inlen, GFP_ATOMIC);
if (!cmd_item)
return -ENOMEM;
kref_init(&cmd_item->ref);
cmd_item->quiet = true;
cmd_item->cookie = cookie;
cmd_item->completer = complete;
cmd_item->cmd = cmd;
cmd_item->inlen = inlen;
/* inbuf is probably not valid after return, so take a copy */
cmd_item->inbuf = (struct cdx_dword *)(cmd_item + 1);
memcpy(cmd_item + 1, inbuf, inlen);
return cdx_mcdi_rpc_async_internal(cdx, cmd_item, NULL);
}
static void _cdx_mcdi_display_error(struct cdx_mcdi *cdx, unsigned int cmd,
size_t inlen, int raw, int arg, int err_no)
{
pr_err("MC command 0x%x inlen %d failed err_no=%d (raw=%d) arg=%d\n",
cmd, (int)inlen, err_no, raw, arg);
}
/*
* Set MCDI mode to fail to prevent any new commands, then cancel any
* outstanding commands.
* Caller must hold the mcdi iface_lock.
*/
static void cdx_mcdi_mode_fail(struct cdx_mcdi *cdx, struct list_head *cleanup_list)
{
struct cdx_mcdi_iface *mcdi = cdx_mcdi_if(cdx);
if (!mcdi)
return;
mcdi->mode = MCDI_MODE_FAIL;
while (!list_empty(&mcdi->cmd_list)) {
struct cdx_mcdi_cmd *cmd;
cmd = list_first_entry(&mcdi->cmd_list, struct cdx_mcdi_cmd,
list);
_cdx_mcdi_cancel_cmd(mcdi, cdx_mcdi_cmd_handle(cmd), cleanup_list);
}
}

View File

@ -0,0 +1,248 @@
/* SPDX-License-Identifier: GPL-2.0
*
* Copyright 2008-2013 Solarflare Communications Inc.
* Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
*/
#ifndef CDX_MCDI_H
#define CDX_MCDI_H
#include <linux/mutex.h>
#include <linux/kref.h>
#include <linux/rpmsg.h>
#include "bitfield.h"
#include "mc_cdx_pcol.h"
#ifdef DEBUG
#define CDX_WARN_ON_ONCE_PARANOID(x) WARN_ON_ONCE(x)
#define CDX_WARN_ON_PARANOID(x) WARN_ON(x)
#else
#define CDX_WARN_ON_ONCE_PARANOID(x) do {} while (0)
#define CDX_WARN_ON_PARANOID(x) do {} while (0)
#endif
/**
* enum cdx_mcdi_mode - MCDI transaction mode
* @MCDI_MODE_EVENTS: wait for an mcdi response callback.
* @MCDI_MODE_FAIL: we think MCDI is dead, so fail-fast all calls
*/
enum cdx_mcdi_mode {
MCDI_MODE_EVENTS,
MCDI_MODE_FAIL,
};
#define MCDI_RPC_TIMEOUT (10 * HZ)
#define MCDI_RPC_LONG_TIMEOU (60 * HZ)
#define MCDI_RPC_POST_RST_TIME (10 * HZ)
#define MCDI_BUF_LEN (8 + MCDI_CTL_SDU_LEN_MAX)
/**
* enum cdx_mcdi_cmd_state - State for an individual MCDI command
* @MCDI_STATE_QUEUED: Command not started and is waiting to run.
* @MCDI_STATE_RETRY: Command was submitted and MC rejected with no resources,
* as MC have too many outstanding commands. Command will be retried once
* another command returns.
* @MCDI_STATE_RUNNING: Command was accepted and is running.
* @MCDI_STATE_RUNNING_CANCELLED: Command is running but the issuer cancelled
* the command.
* @MCDI_STATE_FINISHED: Processing of this command has completed.
*/
enum cdx_mcdi_cmd_state {
MCDI_STATE_QUEUED,
MCDI_STATE_RETRY,
MCDI_STATE_RUNNING,
MCDI_STATE_RUNNING_CANCELLED,
MCDI_STATE_FINISHED,
};
/**
* struct cdx_mcdi - CDX MCDI Firmware interface, to interact
* with CDX controller.
* @mcdi: MCDI interface
* @mcdi_ops: MCDI operations
* @r5_rproc : R5 Remoteproc device handle
* @rpdev: RPMsg device
* @ept: RPMsg endpoint
* @work: Post probe work
*/
struct cdx_mcdi {
/* MCDI interface */
struct cdx_mcdi_data *mcdi;
const struct cdx_mcdi_ops *mcdi_ops;
struct rproc *r5_rproc;
struct rpmsg_device *rpdev;
struct rpmsg_endpoint *ept;
struct work_struct work;
};
struct cdx_mcdi_ops {
void (*mcdi_request)(struct cdx_mcdi *cdx,
const struct cdx_dword *hdr, size_t hdr_len,
const struct cdx_dword *sdu, size_t sdu_len);
unsigned int (*mcdi_rpc_timeout)(struct cdx_mcdi *cdx, unsigned int cmd);
};
typedef void cdx_mcdi_async_completer(struct cdx_mcdi *cdx,
unsigned long cookie, int rc,
struct cdx_dword *outbuf,
size_t outlen_actual);
/**
* struct cdx_mcdi_cmd - An outstanding MCDI command
* @ref: Reference count. There will be one reference if the command is
* in the mcdi_iface cmd_list, another if it's on a cleanup list,
* and a third if it's queued in the work queue.
* @list: The data for this entry in mcdi->cmd_list
* @cleanup_list: The data for this entry in a cleanup list
* @work: The work item for this command, queued in mcdi->workqueue
* @mcdi: The mcdi_iface for this command
* @state: The state of this command
* @inlen: inbuf length
* @inbuf: Input buffer
* @quiet: Whether to silence errors
* @reboot_seen: Whether a reboot has been seen during this command,
* to prevent duplicates
* @seq: Sequence number
* @started: Jiffies this command was started at
* @cookie: Context for completion function
* @completer: Completion function
* @handle: Command handle
* @cmd: Command number
* @rc: Return code
* @outlen: Length of output buffer
* @outbuf: Output buffer
*/
struct cdx_mcdi_cmd {
struct kref ref;
struct list_head list;
struct list_head cleanup_list;
struct work_struct work;
struct cdx_mcdi_iface *mcdi;
enum cdx_mcdi_cmd_state state;
size_t inlen;
const struct cdx_dword *inbuf;
bool quiet;
bool reboot_seen;
u8 seq;
unsigned long started;
unsigned long cookie;
cdx_mcdi_async_completer *completer;
unsigned int handle;
unsigned int cmd;
int rc;
size_t outlen;
struct cdx_dword *outbuf;
/* followed by inbuf data if necessary */
};
/**
* struct cdx_mcdi_iface - MCDI protocol context
* @cdx: The associated NIC
* @iface_lock: Serialise access to this structure
* @outstanding_cleanups: Count of cleanups
* @cmd_list: List of outstanding and running commands
* @workqueue: Workqueue used for delayed processing
* @cmd_complete_wq: Waitqueue for command completion
* @db_held_by: Command the MC doorbell is in use by
* @seq_held_by: Command each sequence number is in use by
* @prev_handle: The last used command handle
* @mode: Poll for mcdi completion, or wait for an mcdi_event
* @prev_seq: The last used sequence number
* @new_epoch: Indicates start of day or start of MC reboot recovery
* @logging_buffer: Buffer that may be used to build MCDI tracing messages
* @logging_enabled: Whether to trace MCDI
*/
struct cdx_mcdi_iface {
struct cdx_mcdi *cdx;
/* Serialise access */
struct mutex iface_lock;
unsigned int outstanding_cleanups;
struct list_head cmd_list;
struct workqueue_struct *workqueue;
wait_queue_head_t cmd_complete_wq;
struct cdx_mcdi_cmd *db_held_by;
struct cdx_mcdi_cmd *seq_held_by[16];
unsigned int prev_handle;
enum cdx_mcdi_mode mode;
u8 prev_seq;
bool new_epoch;
#ifdef CONFIG_MCDI_LOGGING
bool logging_enabled;
char *logging_buffer;
#endif
};
/**
* struct cdx_mcdi_data - extra state for NICs that implement MCDI
* @iface: Interface/protocol state
* @fn_flags: Flags for this function, as returned by %MC_CMD_DRV_ATTACH.
*/
struct cdx_mcdi_data {
struct cdx_mcdi_iface iface;
u32 fn_flags;
};
static inline struct cdx_mcdi_iface *cdx_mcdi_if(struct cdx_mcdi *cdx)
{
return cdx->mcdi ? &cdx->mcdi->iface : NULL;
}
int cdx_mcdi_init(struct cdx_mcdi *cdx);
void cdx_mcdi_finish(struct cdx_mcdi *cdx);
void cdx_mcdi_process_cmd(struct cdx_mcdi *cdx, struct cdx_dword *outbuf, int len);
int cdx_mcdi_rpc(struct cdx_mcdi *cdx, unsigned int cmd,
const struct cdx_dword *inbuf, size_t inlen,
struct cdx_dword *outbuf, size_t outlen, size_t *outlen_actual);
int cdx_mcdi_rpc_async(struct cdx_mcdi *cdx, unsigned int cmd,
const struct cdx_dword *inbuf, size_t inlen,
cdx_mcdi_async_completer *complete,
unsigned long cookie);
int cdx_mcdi_wait_for_quiescence(struct cdx_mcdi *cdx,
unsigned int timeout_jiffies);
/*
* We expect that 16- and 32-bit fields in MCDI requests and responses
* are appropriately aligned, but 64-bit fields are only
* 32-bit-aligned.
*/
#define MCDI_DECLARE_BUF(_name, _len) struct cdx_dword _name[DIV_ROUND_UP(_len, 4)] = {{0}}
#define _MCDI_PTR(_buf, _offset) \
((u8 *)(_buf) + (_offset))
#define MCDI_PTR(_buf, _field) \
_MCDI_PTR(_buf, MC_CMD_ ## _field ## _OFST)
#define _MCDI_CHECK_ALIGN(_ofst, _align) \
((void)BUILD_BUG_ON_ZERO((_ofst) & ((_align) - 1)), \
(_ofst))
#define _MCDI_DWORD(_buf, _field) \
((_buf) + (_MCDI_CHECK_ALIGN(MC_CMD_ ## _field ## _OFST, 4) >> 2))
#define MCDI_BYTE(_buf, _field) \
((void)BUILD_BUG_ON_ZERO(MC_CMD_ ## _field ## _LEN != 1), \
*MCDI_PTR(_buf, _field))
#define MCDI_WORD(_buf, _field) \
((void)BUILD_BUG_ON_ZERO(MC_CMD_ ## _field ## _LEN != 2), \
le16_to_cpu(*(__force const __le16 *)MCDI_PTR(_buf, _field)))
#define MCDI_SET_DWORD(_buf, _field, _value) \
CDX_POPULATE_DWORD_1(*_MCDI_DWORD(_buf, _field), CDX_DWORD, _value)
#define MCDI_DWORD(_buf, _field) \
CDX_DWORD_FIELD(*_MCDI_DWORD(_buf, _field), CDX_DWORD)
#define MCDI_POPULATE_DWORD_1(_buf, _field, _name1, _value1) \
CDX_POPULATE_DWORD_1(*_MCDI_DWORD(_buf, _field), \
MC_CMD_ ## _name1, _value1)
#define MCDI_SET_QWORD(_buf, _field, _value) \
do { \
CDX_POPULATE_DWORD_1(_MCDI_DWORD(_buf, _field)[0], \
CDX_DWORD, (u32)(_value)); \
CDX_POPULATE_DWORD_1(_MCDI_DWORD(_buf, _field)[1], \
CDX_DWORD, (u64)(_value) >> 32); \
} while (0)
#define MCDI_QWORD(_buf, _field) \
(CDX_DWORD_FIELD(_MCDI_DWORD(_buf, _field)[0], CDX_DWORD) | \
(u64)CDX_DWORD_FIELD(_MCDI_DWORD(_buf, _field)[1], CDX_DWORD) << 32)
#endif /* CDX_MCDI_H */

View File

@ -0,0 +1,139 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
*/
#include <linux/module.h>
#include "mcdi.h"
#include "mcdi_functions.h"
int cdx_mcdi_get_num_buses(struct cdx_mcdi *cdx)
{
MCDI_DECLARE_BUF(outbuf, MC_CMD_CDX_BUS_ENUM_BUSES_OUT_LEN);
size_t outlen;
int ret;
ret = cdx_mcdi_rpc(cdx, MC_CMD_CDX_BUS_ENUM_BUSES, NULL, 0,
outbuf, sizeof(outbuf), &outlen);
if (ret)
return ret;
if (outlen != MC_CMD_CDX_BUS_ENUM_BUSES_OUT_LEN)
return -EIO;
return MCDI_DWORD(outbuf, CDX_BUS_ENUM_BUSES_OUT_BUS_COUNT);
}
int cdx_mcdi_get_num_devs(struct cdx_mcdi *cdx, int bus_num)
{
MCDI_DECLARE_BUF(outbuf, MC_CMD_CDX_BUS_ENUM_DEVICES_OUT_LEN);
MCDI_DECLARE_BUF(inbuf, MC_CMD_CDX_BUS_ENUM_DEVICES_IN_LEN);
size_t outlen;
int ret;
MCDI_SET_DWORD(inbuf, CDX_BUS_ENUM_DEVICES_IN_BUS, bus_num);
ret = cdx_mcdi_rpc(cdx, MC_CMD_CDX_BUS_ENUM_DEVICES, inbuf, sizeof(inbuf),
outbuf, sizeof(outbuf), &outlen);
if (ret)
return ret;
if (outlen != MC_CMD_CDX_BUS_ENUM_DEVICES_OUT_LEN)
return -EIO;
return MCDI_DWORD(outbuf, CDX_BUS_ENUM_DEVICES_OUT_DEVICE_COUNT);
}
int cdx_mcdi_get_dev_config(struct cdx_mcdi *cdx,
u8 bus_num, u8 dev_num,
struct cdx_dev_params *dev_params)
{
MCDI_DECLARE_BUF(outbuf, MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_LEN);
MCDI_DECLARE_BUF(inbuf, MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_IN_LEN);
struct resource *res = &dev_params->res[0];
size_t outlen;
u32 req_id;
int ret;
MCDI_SET_DWORD(inbuf, CDX_BUS_GET_DEVICE_CONFIG_IN_BUS, bus_num);
MCDI_SET_DWORD(inbuf, CDX_BUS_GET_DEVICE_CONFIG_IN_DEVICE, dev_num);
ret = cdx_mcdi_rpc(cdx, MC_CMD_CDX_BUS_GET_DEVICE_CONFIG, inbuf, sizeof(inbuf),
outbuf, sizeof(outbuf), &outlen);
if (ret)
return ret;
if (outlen != MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_LEN)
return -EIO;
dev_params->bus_num = bus_num;
dev_params->dev_num = dev_num;
req_id = MCDI_DWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_REQUESTER_ID);
dev_params->req_id = req_id;
dev_params->res_count = 0;
if (MCDI_QWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_SIZE) != 0) {
res[dev_params->res_count].start =
MCDI_QWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_BASE);
res[dev_params->res_count].end =
MCDI_QWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_BASE) +
MCDI_QWORD(outbuf,
CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_SIZE) - 1;
res[dev_params->res_count].flags = IORESOURCE_MEM;
dev_params->res_count++;
}
if (MCDI_QWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_SIZE) != 0) {
res[dev_params->res_count].start =
MCDI_QWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_BASE);
res[dev_params->res_count].end =
MCDI_QWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_BASE) +
MCDI_QWORD(outbuf,
CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION1_SIZE) - 1;
res[dev_params->res_count].flags = IORESOURCE_MEM;
dev_params->res_count++;
}
if (MCDI_QWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_SIZE) != 0) {
res[dev_params->res_count].start =
MCDI_QWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_BASE);
res[dev_params->res_count].end =
MCDI_QWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_BASE) +
MCDI_QWORD(outbuf,
CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION2_SIZE) - 1;
res[dev_params->res_count].flags = IORESOURCE_MEM;
dev_params->res_count++;
}
if (MCDI_QWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_SIZE) != 0) {
res[dev_params->res_count].start =
MCDI_QWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_BASE);
res[dev_params->res_count].end =
MCDI_QWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_BASE) +
MCDI_QWORD(outbuf,
CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION3_SIZE) - 1;
res[dev_params->res_count].flags = IORESOURCE_MEM;
dev_params->res_count++;
}
dev_params->vendor = MCDI_WORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_VENDOR_ID);
dev_params->device = MCDI_WORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_DEVICE_ID);
return 0;
}
int cdx_mcdi_reset_device(struct cdx_mcdi *cdx, u8 bus_num, u8 dev_num)
{
MCDI_DECLARE_BUF(inbuf, MC_CMD_CDX_DEVICE_RESET_IN_LEN);
int ret;
MCDI_SET_DWORD(inbuf, CDX_DEVICE_RESET_IN_BUS, bus_num);
MCDI_SET_DWORD(inbuf, CDX_DEVICE_RESET_IN_DEVICE, dev_num);
ret = cdx_mcdi_rpc(cdx, MC_CMD_CDX_DEVICE_RESET, inbuf, sizeof(inbuf),
NULL, 0, NULL);
return ret;
}

View File

@ -0,0 +1,61 @@
/* SPDX-License-Identifier: GPL-2.0
*
* Header file for MCDI FW interaction for CDX bus.
*
* Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
*/
#ifndef CDX_MCDI_FUNCTIONS_H
#define CDX_MCDI_FUNCTIONS_H
#include "mcdi.h"
#include "../cdx.h"
/**
* cdx_mcdi_get_num_buses - Get the total number of buses on
* the controller.
* @cdx: pointer to MCDI interface.
*
* Return: total number of buses available on the controller,
* <0 on failure
*/
int cdx_mcdi_get_num_buses(struct cdx_mcdi *cdx);
/**
* cdx_mcdi_get_num_devs - Get the total number of devices on
* a particular bus of the controller.
* @cdx: pointer to MCDI interface.
* @bus_num: Bus number.
*
* Return: total number of devices available on the bus, <0 on failure
*/
int cdx_mcdi_get_num_devs(struct cdx_mcdi *cdx, int bus_num);
/**
* cdx_mcdi_get_dev_config - Get configuration for a particular
* bus_num:dev_num
* @cdx: pointer to MCDI interface.
* @bus_num: Bus number.
* @dev_num: Device number.
* @dev_params: Pointer to cdx_dev_params, this is populated by this
* device with the configuration corresponding to the provided
* bus_num:dev_num.
*
* Return: 0 total number of devices available on the bus, <0 on failure
*/
int cdx_mcdi_get_dev_config(struct cdx_mcdi *cdx,
u8 bus_num, u8 dev_num,
struct cdx_dev_params *dev_params);
/**
* cdx_mcdi_reset_device - Reset cdx device represented by bus_num:dev_num
* @cdx: pointer to MCDI interface.
* @bus_num: Bus number.
* @dev_num: Device number.
*
* Return: 0 on success, <0 on failure
*/
int cdx_mcdi_reset_device(struct cdx_mcdi *cdx,
u8 bus_num, u8 dev_num);
#endif /* CDX_MCDI_FUNCTIONS_H */

View File

@ -247,8 +247,6 @@ config SONYPI
To compile this driver as a module, choose M here: the
module will be called sonypi.
source "drivers/char/pcmcia/Kconfig"
config MWAVE
tristate "ACP Modem (Mwave) support"
depends on X86 && TTY

View File

@ -35,7 +35,6 @@ obj-$(CONFIG_TELCLOCK) += tlclk.o
obj-$(CONFIG_MWAVE) += mwave/
obj-y += agp/
obj-$(CONFIG_PCMCIA) += pcmcia/
obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o
obj-$(CONFIG_TCG_TPM) += tpm/

View File

@ -1,68 +0,0 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# PCMCIA character device configuration
#
menu "PCMCIA character devices"
depends on PCMCIA!=n
config SYNCLINK_CS
tristate "SyncLink PC Card support"
depends on PCMCIA && TTY
help
Enable support for the SyncLink PC Card serial adapter, running
asynchronous and HDLC communications up to 512Kbps. The port is
selectable for RS-232, V.35, RS-449, RS-530, and X.21
This driver may be built as a module ( = code which can be
inserted in and removed from the running kernel whenever you want).
The module will be called synclink_cs. If you want to do that, say M
here.
config CARDMAN_4000
tristate "Omnikey Cardman 4000 support"
depends on PCMCIA
select BITREVERSE
help
Enable support for the Omnikey Cardman 4000 PCMCIA Smartcard
reader.
This kernel driver requires additional userspace support, either
by the vendor-provided PC/SC ifd_handler (http://www.omnikey.com/),
or via the cm4000 backend of OpenCT (http://www.opensc-project.org/opensc).
config CARDMAN_4040
tristate "Omnikey CardMan 4040 support"
depends on PCMCIA
help
Enable support for the Omnikey CardMan 4040 PCMCIA Smartcard
reader.
This card is basically a USB CCID device connected to a FIFO
in I/O space. To use the kernel driver, you will need either the
PC/SC ifdhandler provided from the Omnikey homepage
(http://www.omnikey.com/), or a current development version of OpenCT
(http://www.opensc-project.org/opensc).
config SCR24X
tristate "SCR24x Chip Card Interface support"
depends on PCMCIA
help
Enable support for the SCR24x PCMCIA Chip Card Interface.
To compile this driver as a module, choose M here.
The module will be called scr24x_cs..
If unsure say N.
config IPWIRELESS
tristate "IPWireless 3G UMTS PCMCIA card support"
depends on PCMCIA && NETDEVICES && TTY
select PPP
help
This is a driver for 3G UMTS PCMCIA card from IPWireless company. In
some countries (for example Czech Republic, T-Mobile ISP) this card
is shipped for service called UMTS 4G.
endmenu

View File

@ -1,11 +0,0 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# drivers/char/pcmcia/Makefile
#
# Makefile for the Linux PCMCIA char device drivers.
#
obj-$(CONFIG_SYNCLINK_CS) += synclink_cs.o
obj-$(CONFIG_CARDMAN_4000) += cm4000_cs.o
obj-$(CONFIG_CARDMAN_4040) += cm4040_cs.o
obj-$(CONFIG_SCR24X) += scr24x_cs.o

File diff suppressed because it is too large Load Diff

View File

@ -1,684 +0,0 @@
/*
* A driver for the Omnikey PCMCIA smartcard reader CardMan 4040
*
* (c) 2000-2004 Omnikey AG (http://www.omnikey.com/)
*
* (C) 2005-2006 Harald Welte <laforge@gnumonks.org>
* - add support for poll()
* - driver cleanup
* - add waitqueues
* - adhere to linux kernel coding style and policies
* - support 2.6.13 "new style" pcmcia interface
* - add class interface for udev device creation
*
* The device basically is a USB CCID compliant device that has been
* attached to an I/O-Mapped FIFO.
*
* All rights reserved, Dual BSD/GPL Licensed.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/mutex.h>
#include <linux/wait.h>
#include <linux/uaccess.h>
#include <asm/io.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/cisreg.h>
#include <pcmcia/ciscode.h>
#include <pcmcia/ds.h>
#include "cm4040_cs.h"
#define reader_to_dev(x) (&x->p_dev->dev)
/* n (debug level) is ignored */
/* additional debug output may be enabled by re-compiling with
* CM4040_DEBUG set */
/* #define CM4040_DEBUG */
#define DEBUGP(n, rdr, x, args...) do { \
dev_dbg(reader_to_dev(rdr), "%s:" x, \
__func__ , ## args); \
} while (0)
static DEFINE_MUTEX(cm4040_mutex);
#define CCID_DRIVER_BULK_DEFAULT_TIMEOUT (150*HZ)
#define CCID_DRIVER_ASYNC_POWERUP_TIMEOUT (35*HZ)
#define CCID_DRIVER_MINIMUM_TIMEOUT (3*HZ)
#define READ_WRITE_BUFFER_SIZE 512
#define POLL_LOOP_COUNT 1000
/* how often to poll for fifo status change */
#define POLL_PERIOD msecs_to_jiffies(10)
static void reader_release(struct pcmcia_device *link);
static int major;
static struct class *cmx_class;
#define BS_READABLE 0x01
#define BS_WRITABLE 0x02
struct reader_dev {
struct pcmcia_device *p_dev;
wait_queue_head_t devq;
wait_queue_head_t poll_wait;
wait_queue_head_t read_wait;
wait_queue_head_t write_wait;
unsigned long buffer_status;
unsigned long timeout;
unsigned char s_buf[READ_WRITE_BUFFER_SIZE];
unsigned char r_buf[READ_WRITE_BUFFER_SIZE];
struct timer_list poll_timer;
};
static struct pcmcia_device *dev_table[CM_MAX_DEV];
#ifndef CM4040_DEBUG
#define xoutb outb
#define xinb inb
#else
static inline void xoutb(unsigned char val, unsigned short port)
{
pr_debug("outb(val=%.2x,port=%.4x)\n", val, port);
outb(val, port);
}
static inline unsigned char xinb(unsigned short port)
{
unsigned char val;
val = inb(port);
pr_debug("%.2x=inb(%.4x)\n", val, port);
return val;
}
#endif
/* poll the device fifo status register. not to be confused with
* the poll syscall. */
static void cm4040_do_poll(struct timer_list *t)
{
struct reader_dev *dev = from_timer(dev, t, poll_timer);
unsigned int obs = xinb(dev->p_dev->resource[0]->start
+ REG_OFFSET_BUFFER_STATUS);
if ((obs & BSR_BULK_IN_FULL)) {
set_bit(BS_READABLE, &dev->buffer_status);
DEBUGP(4, dev, "waking up read_wait\n");
wake_up_interruptible(&dev->read_wait);
} else
clear_bit(BS_READABLE, &dev->buffer_status);
if (!(obs & BSR_BULK_OUT_FULL)) {
set_bit(BS_WRITABLE, &dev->buffer_status);
DEBUGP(4, dev, "waking up write_wait\n");
wake_up_interruptible(&dev->write_wait);
} else
clear_bit(BS_WRITABLE, &dev->buffer_status);
if (dev->buffer_status)
wake_up_interruptible(&dev->poll_wait);
mod_timer(&dev->poll_timer, jiffies + POLL_PERIOD);
}
static void cm4040_stop_poll(struct reader_dev *dev)
{
del_timer_sync(&dev->poll_timer);
}
static int wait_for_bulk_out_ready(struct reader_dev *dev)
{
int i, rc;
int iobase = dev->p_dev->resource[0]->start;
for (i = 0; i < POLL_LOOP_COUNT; i++) {
if ((xinb(iobase + REG_OFFSET_BUFFER_STATUS)
& BSR_BULK_OUT_FULL) == 0) {
DEBUGP(4, dev, "BulkOut empty (i=%d)\n", i);
return 1;
}
}
DEBUGP(4, dev, "wait_event_interruptible_timeout(timeout=%ld\n",
dev->timeout);
rc = wait_event_interruptible_timeout(dev->write_wait,
test_and_clear_bit(BS_WRITABLE,
&dev->buffer_status),
dev->timeout);
if (rc > 0)
DEBUGP(4, dev, "woke up: BulkOut empty\n");
else if (rc == 0)
DEBUGP(4, dev, "woke up: BulkOut full, returning 0 :(\n");
else if (rc < 0)
DEBUGP(4, dev, "woke up: signal arrived\n");
return rc;
}
/* Write to Sync Control Register */
static int write_sync_reg(unsigned char val, struct reader_dev *dev)
{
int iobase = dev->p_dev->resource[0]->start;
int rc;
rc = wait_for_bulk_out_ready(dev);
if (rc <= 0)
return rc;
xoutb(val, iobase + REG_OFFSET_SYNC_CONTROL);
rc = wait_for_bulk_out_ready(dev);
if (rc <= 0)
return rc;
return 1;
}
static int wait_for_bulk_in_ready(struct reader_dev *dev)
{
int i, rc;
int iobase = dev->p_dev->resource[0]->start;
for (i = 0; i < POLL_LOOP_COUNT; i++) {
if ((xinb(iobase + REG_OFFSET_BUFFER_STATUS)
& BSR_BULK_IN_FULL) == BSR_BULK_IN_FULL) {
DEBUGP(3, dev, "BulkIn full (i=%d)\n", i);
return 1;
}
}
DEBUGP(4, dev, "wait_event_interruptible_timeout(timeout=%ld\n",
dev->timeout);
rc = wait_event_interruptible_timeout(dev->read_wait,
test_and_clear_bit(BS_READABLE,
&dev->buffer_status),
dev->timeout);
if (rc > 0)
DEBUGP(4, dev, "woke up: BulkIn full\n");
else if (rc == 0)
DEBUGP(4, dev, "woke up: BulkIn not full, returning 0 :(\n");
else if (rc < 0)
DEBUGP(4, dev, "woke up: signal arrived\n");
return rc;
}
static ssize_t cm4040_read(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
struct reader_dev *dev = filp->private_data;
int iobase = dev->p_dev->resource[0]->start;
size_t bytes_to_read;
unsigned long i;
size_t min_bytes_to_read;
int rc;
DEBUGP(2, dev, "-> cm4040_read(%s,%d)\n", current->comm, current->pid);
if (count == 0)
return 0;
if (count < 10)
return -EFAULT;
if (filp->f_flags & O_NONBLOCK) {
DEBUGP(4, dev, "filep->f_flags O_NONBLOCK set\n");
DEBUGP(2, dev, "<- cm4040_read (failure)\n");
return -EAGAIN;
}
if (!pcmcia_dev_present(dev->p_dev))
return -ENODEV;
for (i = 0; i < 5; i++) {
rc = wait_for_bulk_in_ready(dev);
if (rc <= 0) {
DEBUGP(5, dev, "wait_for_bulk_in_ready rc=%.2x\n", rc);
DEBUGP(2, dev, "<- cm4040_read (failed)\n");
if (rc == -ERESTARTSYS)
return rc;
return -EIO;
}
dev->r_buf[i] = xinb(iobase + REG_OFFSET_BULK_IN);
#ifdef CM4040_DEBUG
pr_debug("%lu:%2x ", i, dev->r_buf[i]);
}
pr_debug("\n");
#else
}
#endif
bytes_to_read = 5 + le32_to_cpu(*(__le32 *)&dev->r_buf[1]);
DEBUGP(6, dev, "BytesToRead=%zu\n", bytes_to_read);
min_bytes_to_read = min(count, bytes_to_read + 5);
min_bytes_to_read = min_t(size_t, min_bytes_to_read, READ_WRITE_BUFFER_SIZE);
DEBUGP(6, dev, "Min=%zu\n", min_bytes_to_read);
for (i = 0; i < (min_bytes_to_read-5); i++) {
rc = wait_for_bulk_in_ready(dev);
if (rc <= 0) {
DEBUGP(5, dev, "wait_for_bulk_in_ready rc=%.2x\n", rc);
DEBUGP(2, dev, "<- cm4040_read (failed)\n");
if (rc == -ERESTARTSYS)
return rc;
return -EIO;
}
dev->r_buf[i+5] = xinb(iobase + REG_OFFSET_BULK_IN);
#ifdef CM4040_DEBUG
pr_debug("%lu:%2x ", i, dev->r_buf[i]);
}
pr_debug("\n");
#else
}
#endif
*ppos = min_bytes_to_read;
if (copy_to_user(buf, dev->r_buf, min_bytes_to_read))
return -EFAULT;
rc = wait_for_bulk_in_ready(dev);
if (rc <= 0) {
DEBUGP(5, dev, "wait_for_bulk_in_ready rc=%.2x\n", rc);
DEBUGP(2, dev, "<- cm4040_read (failed)\n");
if (rc == -ERESTARTSYS)
return rc;
return -EIO;
}
rc = write_sync_reg(SCR_READER_TO_HOST_DONE, dev);
if (rc <= 0) {
DEBUGP(5, dev, "write_sync_reg c=%.2x\n", rc);
DEBUGP(2, dev, "<- cm4040_read (failed)\n");
if (rc == -ERESTARTSYS)
return rc;
else
return -EIO;
}
xinb(iobase + REG_OFFSET_BULK_IN);
DEBUGP(2, dev, "<- cm4040_read (successfully)\n");
return min_bytes_to_read;
}
static ssize_t cm4040_write(struct file *filp, const char __user *buf,
size_t count, loff_t *ppos)
{
struct reader_dev *dev = filp->private_data;
int iobase = dev->p_dev->resource[0]->start;
ssize_t rc;
int i;
unsigned int bytes_to_write;
DEBUGP(2, dev, "-> cm4040_write(%s,%d)\n", current->comm, current->pid);
if (count == 0) {
DEBUGP(2, dev, "<- cm4040_write empty read (successfully)\n");
return 0;
}
if ((count < 5) || (count > READ_WRITE_BUFFER_SIZE)) {
DEBUGP(2, dev, "<- cm4040_write buffersize=%zd < 5\n", count);
return -EIO;
}
if (filp->f_flags & O_NONBLOCK) {
DEBUGP(4, dev, "filep->f_flags O_NONBLOCK set\n");
DEBUGP(4, dev, "<- cm4040_write (failure)\n");
return -EAGAIN;
}
if (!pcmcia_dev_present(dev->p_dev))
return -ENODEV;
bytes_to_write = count;
if (copy_from_user(dev->s_buf, buf, bytes_to_write))
return -EFAULT;
switch (dev->s_buf[0]) {
case CMD_PC_TO_RDR_XFRBLOCK:
case CMD_PC_TO_RDR_SECURE:
case CMD_PC_TO_RDR_TEST_SECURE:
case CMD_PC_TO_RDR_OK_SECURE:
dev->timeout = CCID_DRIVER_BULK_DEFAULT_TIMEOUT;
break;
case CMD_PC_TO_RDR_ICCPOWERON:
dev->timeout = CCID_DRIVER_ASYNC_POWERUP_TIMEOUT;
break;
case CMD_PC_TO_RDR_GETSLOTSTATUS:
case CMD_PC_TO_RDR_ICCPOWEROFF:
case CMD_PC_TO_RDR_GETPARAMETERS:
case CMD_PC_TO_RDR_RESETPARAMETERS:
case CMD_PC_TO_RDR_SETPARAMETERS:
case CMD_PC_TO_RDR_ESCAPE:
case CMD_PC_TO_RDR_ICCCLOCK:
default:
dev->timeout = CCID_DRIVER_MINIMUM_TIMEOUT;
break;
}
rc = write_sync_reg(SCR_HOST_TO_READER_START, dev);
if (rc <= 0) {
DEBUGP(5, dev, "write_sync_reg c=%.2zx\n", rc);
DEBUGP(2, dev, "<- cm4040_write (failed)\n");
if (rc == -ERESTARTSYS)
return rc;
else
return -EIO;
}
DEBUGP(4, dev, "start \n");
for (i = 0; i < bytes_to_write; i++) {
rc = wait_for_bulk_out_ready(dev);
if (rc <= 0) {
DEBUGP(5, dev, "wait_for_bulk_out_ready rc=%.2zx\n",
rc);
DEBUGP(2, dev, "<- cm4040_write (failed)\n");
if (rc == -ERESTARTSYS)
return rc;
else
return -EIO;
}
xoutb(dev->s_buf[i],iobase + REG_OFFSET_BULK_OUT);
}
DEBUGP(4, dev, "end\n");
rc = write_sync_reg(SCR_HOST_TO_READER_DONE, dev);
if (rc <= 0) {
DEBUGP(5, dev, "write_sync_reg c=%.2zx\n", rc);
DEBUGP(2, dev, "<- cm4040_write (failed)\n");
if (rc == -ERESTARTSYS)
return rc;
else
return -EIO;
}
DEBUGP(2, dev, "<- cm4040_write (successfully)\n");
return count;
}
static __poll_t cm4040_poll(struct file *filp, poll_table *wait)
{
struct reader_dev *dev = filp->private_data;
__poll_t mask = 0;
poll_wait(filp, &dev->poll_wait, wait);
if (test_and_clear_bit(BS_READABLE, &dev->buffer_status))
mask |= EPOLLIN | EPOLLRDNORM;
if (test_and_clear_bit(BS_WRITABLE, &dev->buffer_status))
mask |= EPOLLOUT | EPOLLWRNORM;
DEBUGP(2, dev, "<- cm4040_poll(%u)\n", mask);
return mask;
}
static int cm4040_open(struct inode *inode, struct file *filp)
{
struct reader_dev *dev;
struct pcmcia_device *link;
int minor = iminor(inode);
int ret;
if (minor >= CM_MAX_DEV)
return -ENODEV;
mutex_lock(&cm4040_mutex);
link = dev_table[minor];
if (link == NULL || !pcmcia_dev_present(link)) {
ret = -ENODEV;
goto out;
}
if (link->open) {
ret = -EBUSY;
goto out;
}
dev = link->priv;
filp->private_data = dev;
if (filp->f_flags & O_NONBLOCK) {
DEBUGP(4, dev, "filep->f_flags O_NONBLOCK set\n");
ret = -EAGAIN;
goto out;
}
link->open = 1;
mod_timer(&dev->poll_timer, jiffies + POLL_PERIOD);
DEBUGP(2, dev, "<- cm4040_open (successfully)\n");
ret = nonseekable_open(inode, filp);
out:
mutex_unlock(&cm4040_mutex);
return ret;
}
static int cm4040_close(struct inode *inode, struct file *filp)
{
struct reader_dev *dev = filp->private_data;
struct pcmcia_device *link;
int minor = iminor(inode);
DEBUGP(2, dev, "-> cm4040_close(maj/min=%d.%d)\n", imajor(inode),
iminor(inode));
if (minor >= CM_MAX_DEV)
return -ENODEV;
link = dev_table[minor];
if (link == NULL)
return -ENODEV;
cm4040_stop_poll(dev);
link->open = 0;
wake_up(&dev->devq);
DEBUGP(2, dev, "<- cm4040_close\n");
return 0;
}
static void cm4040_reader_release(struct pcmcia_device *link)
{
struct reader_dev *dev = link->priv;
DEBUGP(3, dev, "-> cm4040_reader_release\n");
while (link->open) {
DEBUGP(3, dev, MODULE_NAME ": delaying release "
"until process has terminated\n");
wait_event(dev->devq, (link->open == 0));
}
DEBUGP(3, dev, "<- cm4040_reader_release\n");
return;
}
static int cm4040_config_check(struct pcmcia_device *p_dev, void *priv_data)
{
return pcmcia_request_io(p_dev);
}
static int reader_config(struct pcmcia_device *link, int devno)
{
struct reader_dev *dev;
int fail_rc;
link->config_flags |= CONF_AUTO_SET_IO;
if (pcmcia_loop_config(link, cm4040_config_check, NULL))
goto cs_release;
fail_rc = pcmcia_enable_device(link);
if (fail_rc != 0) {
dev_info(&link->dev, "pcmcia_enable_device failed 0x%x\n",
fail_rc);
goto cs_release;
}
dev = link->priv;
DEBUGP(2, dev, "device " DEVICE_NAME "%d at %pR\n", devno,
link->resource[0]);
DEBUGP(2, dev, "<- reader_config (succ)\n");
return 0;
cs_release:
reader_release(link);
return -ENODEV;
}
static void reader_release(struct pcmcia_device *link)
{
cm4040_reader_release(link);
pcmcia_disable_device(link);
}
static int reader_probe(struct pcmcia_device *link)
{
struct reader_dev *dev;
int i, ret;
for (i = 0; i < CM_MAX_DEV; i++) {
if (dev_table[i] == NULL)
break;
}
if (i == CM_MAX_DEV)
return -ENODEV;
dev = kzalloc(sizeof(struct reader_dev), GFP_KERNEL);
if (dev == NULL)
return -ENOMEM;
dev->timeout = CCID_DRIVER_MINIMUM_TIMEOUT;
dev->buffer_status = 0;
link->priv = dev;
dev->p_dev = link;
dev_table[i] = link;
init_waitqueue_head(&dev->devq);
init_waitqueue_head(&dev->poll_wait);
init_waitqueue_head(&dev->read_wait);
init_waitqueue_head(&dev->write_wait);
timer_setup(&dev->poll_timer, cm4040_do_poll, 0);
ret = reader_config(link, i);
if (ret) {
dev_table[i] = NULL;
kfree(dev);
return ret;
}
device_create(cmx_class, NULL, MKDEV(major, i), NULL, "cmx%d", i);
return 0;
}
static void reader_detach(struct pcmcia_device *link)
{
struct reader_dev *dev = link->priv;
int devno;
/* find device */
for (devno = 0; devno < CM_MAX_DEV; devno++) {
if (dev_table[devno] == link)
break;
}
if (devno == CM_MAX_DEV)
return;
reader_release(link);
dev_table[devno] = NULL;
kfree(dev);
device_destroy(cmx_class, MKDEV(major, devno));
return;
}
static const struct file_operations reader_fops = {
.owner = THIS_MODULE,
.read = cm4040_read,
.write = cm4040_write,
.open = cm4040_open,
.release = cm4040_close,
.poll = cm4040_poll,
.llseek = no_llseek,
};
static const struct pcmcia_device_id cm4040_ids[] = {
PCMCIA_DEVICE_MANF_CARD(0x0223, 0x0200),
PCMCIA_DEVICE_PROD_ID12("OMNIKEY", "CardMan 4040",
0xE32CDD8C, 0x8F23318B),
PCMCIA_DEVICE_NULL,
};
MODULE_DEVICE_TABLE(pcmcia, cm4040_ids);
static struct pcmcia_driver reader_driver = {
.owner = THIS_MODULE,
.name = "cm4040_cs",
.probe = reader_probe,
.remove = reader_detach,
.id_table = cm4040_ids,
};
static int __init cm4040_init(void)
{
int rc;
cmx_class = class_create("cardman_4040");
if (IS_ERR(cmx_class))
return PTR_ERR(cmx_class);
major = register_chrdev(0, DEVICE_NAME, &reader_fops);
if (major < 0) {
printk(KERN_WARNING MODULE_NAME
": could not get major number\n");
class_destroy(cmx_class);
return major;
}
rc = pcmcia_register_driver(&reader_driver);
if (rc < 0) {
unregister_chrdev(major, DEVICE_NAME);
class_destroy(cmx_class);
return rc;
}
return 0;
}
static void __exit cm4040_exit(void)
{
pcmcia_unregister_driver(&reader_driver);
unregister_chrdev(major, DEVICE_NAME);
class_destroy(cmx_class);
}
module_init(cm4040_init);
module_exit(cm4040_exit);
MODULE_LICENSE("Dual BSD/GPL");

View File

@ -1,48 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _CM4040_H_
#define _CM4040_H_
#define CM_MAX_DEV 4
#define DEVICE_NAME "cmx"
#define MODULE_NAME "cm4040_cs"
#define REG_OFFSET_BULK_OUT 0
#define REG_OFFSET_BULK_IN 0
#define REG_OFFSET_BUFFER_STATUS 1
#define REG_OFFSET_SYNC_CONTROL 2
#define BSR_BULK_IN_FULL 0x02
#define BSR_BULK_OUT_FULL 0x01
#define SCR_HOST_TO_READER_START 0x80
#define SCR_ABORT 0x40
#define SCR_EN_NOTIFY 0x20
#define SCR_ACK_NOTIFY 0x10
#define SCR_READER_TO_HOST_DONE 0x08
#define SCR_HOST_TO_READER_DONE 0x04
#define SCR_PULSE_INTERRUPT 0x02
#define SCR_POWER_DOWN 0x01
#define CMD_PC_TO_RDR_ICCPOWERON 0x62
#define CMD_PC_TO_RDR_GETSLOTSTATUS 0x65
#define CMD_PC_TO_RDR_ICCPOWEROFF 0x63
#define CMD_PC_TO_RDR_SECURE 0x69
#define CMD_PC_TO_RDR_GETPARAMETERS 0x6C
#define CMD_PC_TO_RDR_RESETPARAMETERS 0x6D
#define CMD_PC_TO_RDR_SETPARAMETERS 0x61
#define CMD_PC_TO_RDR_XFRBLOCK 0x6F
#define CMD_PC_TO_RDR_ESCAPE 0x6B
#define CMD_PC_TO_RDR_ICCCLOCK 0x6E
#define CMD_PC_TO_RDR_TEST_SECURE 0x74
#define CMD_PC_TO_RDR_OK_SECURE 0x89
#define CMD_RDR_TO_PC_SLOTSTATUS 0x81
#define CMD_RDR_TO_PC_DATABLOCK 0x80
#define CMD_RDR_TO_PC_PARAMETERS 0x82
#define CMD_RDR_TO_PC_ESCAPE 0x83
#define CMD_RDR_TO_PC_OK_SECURE 0x89
#endif /* _CM4040_H_ */

View File

@ -1,359 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* SCR24x PCMCIA Smart Card Reader Driver
*
* Copyright (C) 2005-2006 TL Sudheendran
* Copyright (C) 2016 Lubomir Rintel
*
* Derived from "scr24x_v4.2.6_Release.tar.gz" driver by TL Sudheendran.
*/
#include <linux/device.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/ds.h>
#define CCID_HEADER_SIZE 10
#define CCID_LENGTH_OFFSET 1
#define CCID_MAX_LEN 271
#define SCR24X_DATA(n) (1 + n)
#define SCR24X_CMD_STATUS 7
#define CMD_START 0x40
#define CMD_WRITE_BYTE 0x41
#define CMD_READ_BYTE 0x42
#define STATUS_BUSY 0x80
struct scr24x_dev {
struct device *dev;
struct cdev c_dev;
unsigned char buf[CCID_MAX_LEN];
int devno;
struct mutex lock;
struct kref refcnt;
u8 __iomem *regs;
};
#define SCR24X_DEVS 8
static DECLARE_BITMAP(scr24x_minors, SCR24X_DEVS);
static struct class *scr24x_class;
static dev_t scr24x_devt;
static void scr24x_delete(struct kref *kref)
{
struct scr24x_dev *dev = container_of(kref, struct scr24x_dev,
refcnt);
kfree(dev);
}
static int scr24x_wait_ready(struct scr24x_dev *dev)
{
u_char status;
int timeout = 100;
do {
status = ioread8(dev->regs + SCR24X_CMD_STATUS);
if (!(status & STATUS_BUSY))
return 0;
msleep(20);
} while (--timeout);
return -EIO;
}
static int scr24x_open(struct inode *inode, struct file *filp)
{
struct scr24x_dev *dev = container_of(inode->i_cdev,
struct scr24x_dev, c_dev);
kref_get(&dev->refcnt);
filp->private_data = dev;
return stream_open(inode, filp);
}
static int scr24x_release(struct inode *inode, struct file *filp)
{
struct scr24x_dev *dev = filp->private_data;
/* We must not take the dev->lock here as scr24x_delete()
* might be called to remove the dev structure altogether.
* We don't need the lock anyway, since after the reference
* acquired in probe() is released in remove() the chrdev
* is already unregistered and noone can possibly acquire
* a reference via open() anymore. */
kref_put(&dev->refcnt, scr24x_delete);
return 0;
}
static int read_chunk(struct scr24x_dev *dev, size_t offset, size_t limit)
{
size_t i, y;
int ret;
for (i = offset; i < limit; i += 5) {
iowrite8(CMD_READ_BYTE, dev->regs + SCR24X_CMD_STATUS);
ret = scr24x_wait_ready(dev);
if (ret < 0)
return ret;
for (y = 0; y < 5 && i + y < limit; y++)
dev->buf[i + y] = ioread8(dev->regs + SCR24X_DATA(y));
}
return 0;
}
static ssize_t scr24x_read(struct file *filp, char __user *buf, size_t count,
loff_t *ppos)
{
struct scr24x_dev *dev = filp->private_data;
int ret;
int len;
if (count < CCID_HEADER_SIZE)
return -EINVAL;
if (mutex_lock_interruptible(&dev->lock))
return -ERESTARTSYS;
if (!dev->dev) {
ret = -ENODEV;
goto out;
}
ret = scr24x_wait_ready(dev);
if (ret < 0)
goto out;
len = CCID_HEADER_SIZE;
ret = read_chunk(dev, 0, len);
if (ret < 0)
goto out;
len += le32_to_cpu(*(__le32 *)(&dev->buf[CCID_LENGTH_OFFSET]));
if (len > sizeof(dev->buf)) {
ret = -EIO;
goto out;
}
ret = read_chunk(dev, CCID_HEADER_SIZE, len);
if (ret < 0)
goto out;
if (len < count)
count = len;
if (copy_to_user(buf, dev->buf, count)) {
ret = -EFAULT;
goto out;
}
ret = count;
out:
mutex_unlock(&dev->lock);
return ret;
}
static ssize_t scr24x_write(struct file *filp, const char __user *buf,
size_t count, loff_t *ppos)
{
struct scr24x_dev *dev = filp->private_data;
size_t i, y;
int ret;
if (mutex_lock_interruptible(&dev->lock))
return -ERESTARTSYS;
if (!dev->dev) {
ret = -ENODEV;
goto out;
}
if (count > sizeof(dev->buf)) {
ret = -EINVAL;
goto out;
}
if (copy_from_user(dev->buf, buf, count)) {
ret = -EFAULT;
goto out;
}
ret = scr24x_wait_ready(dev);
if (ret < 0)
goto out;
iowrite8(CMD_START, dev->regs + SCR24X_CMD_STATUS);
ret = scr24x_wait_ready(dev);
if (ret < 0)
goto out;
for (i = 0; i < count; i += 5) {
for (y = 0; y < 5 && i + y < count; y++)
iowrite8(dev->buf[i + y], dev->regs + SCR24X_DATA(y));
iowrite8(CMD_WRITE_BYTE, dev->regs + SCR24X_CMD_STATUS);
ret = scr24x_wait_ready(dev);
if (ret < 0)
goto out;
}
ret = count;
out:
mutex_unlock(&dev->lock);
return ret;
}
static const struct file_operations scr24x_fops = {
.owner = THIS_MODULE,
.read = scr24x_read,
.write = scr24x_write,
.open = scr24x_open,
.release = scr24x_release,
.llseek = no_llseek,
};
static int scr24x_config_check(struct pcmcia_device *link, void *priv_data)
{
if (resource_size(link->resource[PCMCIA_IOPORT_0]) != 0x11)
return -ENODEV;
return pcmcia_request_io(link);
}
static int scr24x_probe(struct pcmcia_device *link)
{
struct scr24x_dev *dev;
int ret;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
dev->devno = find_first_zero_bit(scr24x_minors, SCR24X_DEVS);
if (dev->devno >= SCR24X_DEVS) {
ret = -EBUSY;
goto err;
}
mutex_init(&dev->lock);
kref_init(&dev->refcnt);
link->priv = dev;
link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
ret = pcmcia_loop_config(link, scr24x_config_check, NULL);
if (ret < 0)
goto err;
dev->dev = &link->dev;
dev->regs = devm_ioport_map(&link->dev,
link->resource[PCMCIA_IOPORT_0]->start,
resource_size(link->resource[PCMCIA_IOPORT_0]));
if (!dev->regs) {
ret = -EIO;
goto err;
}
cdev_init(&dev->c_dev, &scr24x_fops);
dev->c_dev.owner = THIS_MODULE;
ret = cdev_add(&dev->c_dev, MKDEV(MAJOR(scr24x_devt), dev->devno), 1);
if (ret < 0)
goto err;
ret = pcmcia_enable_device(link);
if (ret < 0) {
pcmcia_disable_device(link);
goto err;
}
device_create(scr24x_class, NULL, MKDEV(MAJOR(scr24x_devt), dev->devno),
NULL, "scr24x%d", dev->devno);
dev_info(&link->dev, "SCR24x Chip Card Interface\n");
return 0;
err:
if (dev->devno < SCR24X_DEVS)
clear_bit(dev->devno, scr24x_minors);
kfree (dev);
return ret;
}
static void scr24x_remove(struct pcmcia_device *link)
{
struct scr24x_dev *dev = (struct scr24x_dev *)link->priv;
device_destroy(scr24x_class, MKDEV(MAJOR(scr24x_devt), dev->devno));
mutex_lock(&dev->lock);
pcmcia_disable_device(link);
cdev_del(&dev->c_dev);
clear_bit(dev->devno, scr24x_minors);
dev->dev = NULL;
mutex_unlock(&dev->lock);
kref_put(&dev->refcnt, scr24x_delete);
}
static const struct pcmcia_device_id scr24x_ids[] = {
PCMCIA_DEVICE_PROD_ID12("HP", "PC Card Smart Card Reader",
0x53cb94f9, 0xbfdf89a5),
PCMCIA_DEVICE_PROD_ID1("SCR241 PCMCIA", 0x6271efa3),
PCMCIA_DEVICE_PROD_ID1("SCR243 PCMCIA", 0x2054e8de),
PCMCIA_DEVICE_PROD_ID1("SCR24x PCMCIA", 0x54a33665),
PCMCIA_DEVICE_NULL
};
MODULE_DEVICE_TABLE(pcmcia, scr24x_ids);
static struct pcmcia_driver scr24x_driver = {
.owner = THIS_MODULE,
.name = "scr24x_cs",
.probe = scr24x_probe,
.remove = scr24x_remove,
.id_table = scr24x_ids,
};
static int __init scr24x_init(void)
{
int ret;
scr24x_class = class_create("scr24x");
if (IS_ERR(scr24x_class))
return PTR_ERR(scr24x_class);
ret = alloc_chrdev_region(&scr24x_devt, 0, SCR24X_DEVS, "scr24x");
if (ret < 0) {
class_destroy(scr24x_class);
return ret;
}
ret = pcmcia_register_driver(&scr24x_driver);
if (ret < 0) {
unregister_chrdev_region(scr24x_devt, SCR24X_DEVS);
class_destroy(scr24x_class);
}
return ret;
}
static void __exit scr24x_exit(void)
{
pcmcia_unregister_driver(&scr24x_driver);
unregister_chrdev_region(scr24x_devt, SCR24X_DEVS);
class_destroy(scr24x_class);
}
module_init(scr24x_init);
module_exit(scr24x_exit);
MODULE_AUTHOR("Lubomir Rintel");
MODULE_DESCRIPTION("SCR24x PCMCIA Smart Card Reader Driver");
MODULE_LICENSE("GPL");

File diff suppressed because it is too large Load Diff

View File

@ -304,7 +304,7 @@ static struct attribute *dmi_sysfs_sel_attrs[] = {
};
ATTRIBUTE_GROUPS(dmi_sysfs_sel);
static struct kobj_type dmi_system_event_log_ktype = {
static const struct kobj_type dmi_system_event_log_ktype = {
.release = dmi_entry_free,
.sysfs_ops = &dmi_sysfs_specialize_attr_ops,
.default_groups = dmi_sysfs_sel_groups,
@ -563,7 +563,7 @@ static void dmi_sysfs_entry_release(struct kobject *kobj)
kfree(entry);
}
static struct kobj_type dmi_sysfs_entry_ktype = {
static const struct kobj_type dmi_sysfs_entry_ktype = {
.release = dmi_sysfs_entry_release,
.sysfs_ops = &dmi_sysfs_attr_ops,
.default_groups = dmi_sysfs_entry_groups,

View File

@ -608,7 +608,7 @@ static void edd_release(struct kobject * kobj)
kfree(dev);
}
static struct kobj_type edd_ktype = {
static const struct kobj_type edd_ktype = {
.release = edd_release,
.sysfs_ops = &edd_attr_ops,
};

View File

@ -1133,8 +1133,8 @@ static int stratix10_svc_drv_probe(struct platform_device *pdev)
return ret;
genpool = svc_create_memory_pool(pdev, sh_memory);
if (!genpool)
return -ENOMEM;
if (IS_ERR(genpool))
return PTR_ERR(genpool);
/* allocate service controller and supporting channel */
controller = devm_kzalloc(dev, sizeof(*controller), GFP_KERNEL);

View File

@ -971,6 +971,39 @@ int zynqmp_pm_fpga_get_status(u32 *value)
}
EXPORT_SYMBOL_GPL(zynqmp_pm_fpga_get_status);
/**
* zynqmp_pm_fpga_get_config_status - Get the FPGA configuration status.
* @value: Buffer to store FPGA configuration status.
*
* This function provides access to the pmufw to get the FPGA configuration
* status
*
* Return: 0 on success, a negative value on error
*/
int zynqmp_pm_fpga_get_config_status(u32 *value)
{
u32 ret_payload[PAYLOAD_ARG_CNT];
u32 buf, lower_addr, upper_addr;
int ret;
if (!value)
return -EINVAL;
lower_addr = lower_32_bits((u64)&buf);
upper_addr = upper_32_bits((u64)&buf);
ret = zynqmp_pm_invoke_fn(PM_FPGA_READ,
XILINX_ZYNQMP_PM_FPGA_CONFIG_STAT_OFFSET,
lower_addr, upper_addr,
XILINX_ZYNQMP_PM_FPGA_READ_CONFIG_REG,
ret_payload);
*value = ret_payload[1];
return ret;
}
EXPORT_SYMBOL_GPL(zynqmp_pm_fpga_get_config_status);
/**
* zynqmp_pm_pinctrl_request - Request Pin from firmware
* @pin: Pin number to request

View File

@ -115,7 +115,7 @@ static int fpga_bridge_dev_match(struct device *dev, const void *data)
/**
* fpga_bridge_get - get an exclusive reference to an fpga bridge
* @dev: parent device that fpga bridge was registered with
* @info: fpga manager info
* @info: fpga image specific information
*
* Given a device, get an exclusive reference to an fpga bridge.
*

View File

@ -77,6 +77,26 @@ static enum fpga_mgr_states zynqmp_fpga_ops_state(struct fpga_manager *mgr)
return FPGA_MGR_STATE_UNKNOWN;
}
static ssize_t status_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
u32 status;
int ret;
ret = zynqmp_pm_fpga_get_config_status(&status);
if (ret)
return ret;
return sysfs_emit(buf, "0x%x\n", status);
}
static DEVICE_ATTR_RO(status);
static struct attribute *zynqmp_fpga_attrs[] = {
&dev_attr_status.attr,
NULL,
};
ATTRIBUTE_GROUPS(zynqmp_fpga);
static const struct fpga_manager_ops zynqmp_fpga_ops = {
.state = zynqmp_fpga_ops_state,
.write_init = zynqmp_fpga_ops_write_init,
@ -113,6 +133,7 @@ static struct platform_driver zynqmp_fpga_driver = {
.driver = {
.name = "zynqmp_fpga_manager",
.of_match_table = of_match_ptr(zynqmp_fpga_of_match),
.dev_groups = zynqmp_fpga_groups,
},
};

View File

@ -160,7 +160,7 @@ of_mipi_dsi_device_add(struct mipi_dsi_host *host, struct device_node *node)
int ret;
u32 reg;
if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) {
if (of_alias_from_compatible(node, info.type, sizeof(info.type)) < 0) {
drm_err(host, "modalias failure on %pOF\n", node);
return ERR_PTR(-EINVAL);
}

View File

@ -207,7 +207,7 @@ static void hsi_add_client_from_dt(struct hsi_port *port,
if (!cl)
return;
err = of_modalias_node(client, name, sizeof(name));
err = of_alias_from_compatible(client, name, sizeof(name));
if (err)
goto err;

View File

@ -901,6 +901,7 @@ int __init etm_perf_init(void)
etm_pmu.addr_filters_sync = etm_addr_filters_sync;
etm_pmu.addr_filters_validate = etm_addr_filters_validate;
etm_pmu.nr_addr_filters = ETM_ADDR_CMP_MAX;
etm_pmu.module = THIS_MODULE;
ret = perf_pmu_register(&etm_pmu, CORESIGHT_ETM_PMU_NAME, -1);
if (ret == 0)

View File

@ -284,7 +284,7 @@ static bool i2c_powermac_get_type(struct i2c_adapter *adap,
*/
/* First try proper modalias */
if (of_modalias_node(node, tmp, sizeof(tmp)) >= 0) {
if (of_alias_from_compatible(node, tmp, sizeof(tmp)) >= 0) {
snprintf(type, type_size, "MAC,%s", tmp);
return true;
}

View File

@ -27,7 +27,7 @@ int of_i2c_get_board_info(struct device *dev, struct device_node *node,
memset(info, 0, sizeof(*info));
if (of_modalias_node(node, info->type, sizeof(info->type)) < 0) {
if (of_alias_from_compatible(node, info->type, sizeof(info->type)) < 0) {
dev_err(dev, "of_i2c: modalias failure on %pOF\n", node);
return -EINVAL;
}

View File

@ -30,6 +30,9 @@ config IIO_CONFIGFS
(e.g. software triggers). For more info see
Documentation/iio/iio_configfs.rst.
config IIO_GTS_HELPER
tristate
config IIO_TRIGGER
bool "Enable triggered sampling support"
help

View File

@ -9,6 +9,7 @@ industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o
industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
obj-$(CONFIG_IIO_CONFIGFS) += industrialio-configfs.o
obj-$(CONFIG_IIO_GTS_HELPER) += industrialio-gts-helper.o
obj-$(CONFIG_IIO_SW_DEVICE) += industrialio-sw-device.o
obj-$(CONFIG_IIO_SW_TRIGGER) += industrialio-sw-trigger.o
obj-$(CONFIG_IIO_TRIGGERED_EVENT) += industrialio-triggered-event.o

View File

@ -1688,7 +1688,7 @@ static irqreturn_t bma400_interrupt(int irq, void *private)
if (FIELD_GET(BMA400_INT_DRDY_MSK, le16_to_cpu(data->status))) {
mutex_unlock(&data->mutex);
iio_trigger_poll_chained(data->trig);
iio_trigger_poll_nested(data->trig);
return IRQ_HANDLED;
}

View File

@ -162,7 +162,6 @@ struct kx022a_data {
int inc_reg;
int ien_reg;
unsigned int g_range;
unsigned int state;
unsigned int odr_ns;
@ -900,7 +899,7 @@ static irqreturn_t kx022a_irq_thread_handler(int irq, void *private)
mutex_lock(&data->mutex);
if (data->trigger_enabled) {
iio_trigger_poll_chained(data->trig);
iio_trigger_poll_nested(data->trig);
ret = IRQ_HANDLED;
}

View File

@ -1067,7 +1067,7 @@ static irqreturn_t mma8452_interrupt(int irq, void *p)
return IRQ_NONE;
if (src & MMA8452_INT_DRDY) {
iio_trigger_poll_chained(indio_dev->trig);
iio_trigger_poll_nested(indio_dev->trig);
ret = IRQ_HANDLED;
}

View File

@ -951,7 +951,7 @@ static irqreturn_t msa311_irq_thread(int irq, void *p)
}
if (new_data_int_enabled)
iio_trigger_poll_chained(msa311->new_data_trig);
iio_trigger_poll_nested(msa311->new_data_trig);
return IRQ_HANDLED;
}

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