mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 04:18:39 +08:00
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:
commit
cec24b8b6b
5
CREDITS
5
CREDITS
@ -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
|
||||
|
56
Documentation/ABI/testing/sysfs-bus-cdx
Normal file
56
Documentation/ABI/testing/sysfs-bus-cdx
Normal 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
|
@ -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
|
||||
|
@ -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
|
||||
|
73
Documentation/ABI/testing/sysfs-driver-zynqmp-fpga
Normal file
73
Documentation/ABI/testing/sysfs-driver-zynqmp-fpga
Normal 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.
|
@ -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;
|
||||
};
|
@ -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>;
|
||||
};
|
||||
};
|
||||
...
|
||||
|
46
Documentation/devicetree/bindings/iio/adc/ti,ads1100.yaml
Normal file
46
Documentation/devicetree/bindings/iio/adc/ti,ads1100.yaml
Normal 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>;
|
||||
};
|
||||
};
|
||||
...
|
@ -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
|
||||
|
||||
|
@ -46,6 +46,9 @@ properties:
|
||||
- items:
|
||||
- const: st,ism330is
|
||||
- const: st,lsm6dso16is
|
||||
- items:
|
||||
- const: st,asm330lhb
|
||||
- const: st,asm330lhh
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
@ -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>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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>;
|
||||
|
@ -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
|
||||
|
@ -11,7 +11,7 @@ maintainers:
|
||||
- Maxime Ripard <mripard@kernel.org>
|
||||
|
||||
allOf:
|
||||
- $ref: "nvmem.yaml#"
|
||||
- $ref: nvmem.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
|
@ -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>;
|
||||
};
|
||||
};
|
@ -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>;
|
||||
};
|
||||
};
|
@ -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 = <ð_mac>;
|
||||
nvmem-cell-names = "eth_mac";
|
||||
};
|
@ -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";
|
||||
};
|
@ -15,7 +15,7 @@ maintainers:
|
||||
- Sven Peter <sven@svenpeter.dev>
|
||||
|
||||
allOf:
|
||||
- $ref: "nvmem.yaml#"
|
||||
- $ref: nvmem.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
|
@ -20,7 +20,7 @@ maintainers:
|
||||
- Rafał Miłecki <rafal@milecki.pl>
|
||||
|
||||
allOf:
|
||||
- $ref: "nvmem.yaml#"
|
||||
- $ref: nvmem.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
|
@ -14,7 +14,7 @@ description: |
|
||||
unique identifier per part.
|
||||
|
||||
allOf:
|
||||
- $ref: "nvmem.yaml#"
|
||||
- $ref: nvmem.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -10,7 +10,7 @@ maintainers:
|
||||
- PrasannaKumar Muralidharan <prasannatsmkumar@gmail.com>
|
||||
|
||||
allOf:
|
||||
- $ref: "nvmem.yaml#"
|
||||
- $ref: nvmem.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
|
@ -61,7 +61,7 @@ properties:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
|
||||
platforn-name:
|
||||
platform-name:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
|
||||
|
@ -15,7 +15,7 @@ maintainers:
|
||||
- Lala Lin <lala.lin@mediatek.com>
|
||||
|
||||
allOf:
|
||||
- $ref: "nvmem.yaml#"
|
||||
- $ref: nvmem.yaml#
|
||||
|
||||
properties:
|
||||
$nodename:
|
||||
|
@ -15,7 +15,7 @@ description: |
|
||||
settings, chip identifiers) or user specific data could be stored.
|
||||
|
||||
allOf:
|
||||
- $ref: "nvmem.yaml#"
|
||||
- $ref: nvmem.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
|
@ -10,7 +10,7 @@ maintainers:
|
||||
- Anson Huang <Anson.Huang@nxp.com>
|
||||
|
||||
allOf:
|
||||
- $ref: "nvmem.yaml#"
|
||||
- $ref: nvmem.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
|
@ -17,7 +17,7 @@ maintainers:
|
||||
- Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
|
||||
|
||||
allOf:
|
||||
- $ref: "nvmem.yaml#"
|
||||
- $ref: nvmem.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
|
@ -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
|
||||
|
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
||||
|
@ -10,7 +10,7 @@ maintainers:
|
||||
- Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
|
||||
|
||||
allOf:
|
||||
- $ref: "nvmem.yaml#"
|
||||
- $ref: nvmem.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
|
@ -10,7 +10,7 @@ maintainers:
|
||||
- Heiko Stuebner <heiko@sntech.de>
|
||||
|
||||
allOf:
|
||||
- $ref: "nvmem.yaml#"
|
||||
- $ref: nvmem.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
|
@ -11,7 +11,7 @@ maintainers:
|
||||
- Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
|
||||
|
||||
allOf:
|
||||
- $ref: "nvmem.yaml#"
|
||||
- $ref: nvmem.yaml#
|
||||
|
||||
properties:
|
||||
"#address-cells": true
|
||||
|
@ -16,7 +16,7 @@ maintainers:
|
||||
- Fabrice Gasnier <fabrice.gasnier@foss.st.com>
|
||||
|
||||
allOf:
|
||||
- $ref: "nvmem.yaml#"
|
||||
- $ref: nvmem.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
|
@ -11,7 +11,7 @@ maintainers:
|
||||
- Vincent Shih <vincent.sunplus@gmail.com>
|
||||
|
||||
allOf:
|
||||
- $ref: "nvmem.yaml#"
|
||||
- $ref: nvmem.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
|
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
44
Documentation/devicetree/bindings/w1/maxim,ds2482.yaml
Normal file
44
Documentation/devicetree/bindings/w1/maxim,ds2482.yaml
Normal 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>;
|
||||
};
|
||||
};
|
@ -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.
|
||||
|
@ -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``
|
||||
|
@ -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``
|
||||
|
@ -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``
|
||||
|
@ -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``
|
||||
|
@ -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``
|
||||
|
@ -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!
|
||||
|
59
MAINTAINERS
59
MAINTAINERS
@ -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/
|
||||
|
@ -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
|
||||
|
@ -241,4 +241,6 @@ source "drivers/peci/Kconfig"
|
||||
|
||||
source "drivers/hte/Kconfig"
|
||||
|
||||
source "drivers/cdx/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
@ -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/
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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
19
drivers/cdx/Kconfig
Normal 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
8
drivers/cdx/Makefile
Normal 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
535
drivers/cdx/cdx.c
Normal 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
62
drivers/cdx/cdx.h
Normal 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_ */
|
31
drivers/cdx/controller/Kconfig
Normal file
31
drivers/cdx/controller/Kconfig
Normal 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
|
9
drivers/cdx/controller/Makefile
Normal file
9
drivers/cdx/controller/Makefile
Normal 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
|
90
drivers/cdx/controller/bitfield.h
Normal file
90
drivers/cdx/controller/bitfield.h
Normal 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 */
|
230
drivers/cdx/controller/cdx_controller.c
Normal file
230
drivers/cdx/controller/cdx_controller.c
Normal 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");
|
30
drivers/cdx/controller/cdx_controller.h
Normal file
30
drivers/cdx/controller/cdx_controller.h
Normal 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_ */
|
202
drivers/cdx/controller/cdx_rpmsg.c
Normal file
202
drivers/cdx/controller/cdx_rpmsg.c
Normal 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);
|
||||
}
|
590
drivers/cdx/controller/mc_cdx_pcol.h
Normal file
590
drivers/cdx/controller/mc_cdx_pcol.h
Normal 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 */
|
903
drivers/cdx/controller/mcdi.c
Normal file
903
drivers/cdx/controller/mcdi.c
Normal 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);
|
||||
}
|
||||
}
|
248
drivers/cdx/controller/mcdi.h
Normal file
248
drivers/cdx/controller/mcdi.h
Normal 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 */
|
139
drivers/cdx/controller/mcdi_functions.c
Normal file
139
drivers/cdx/controller/mcdi_functions.c
Normal 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;
|
||||
}
|
61
drivers/cdx/controller/mcdi_functions.h
Normal file
61
drivers/cdx/controller/mcdi_functions.h
Normal 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 */
|
@ -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
|
||||
|
@ -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/
|
||||
|
@ -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
|
||||
|
@ -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
@ -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");
|
@ -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_ */
|
@ -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
@ -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,
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user