Merge 9055c64567 ("Merge tag 'memblock-v7.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rppt/memblock") into android-mainline

Steps on the way to v7.1-rc1

Change-Id: I467f8ab21f2970b8d4b15884a90972aa363c014b
Signed-off-by: Carlos Llamas <cmllamas@google.com>
This commit is contained in:
Carlos Llamas authored and android-test-infra-autosubmit@system.gserviceaccount.com committed 2026-05-22 14:13:06 -07:00
commit a612f4fecb
505 files changed
+49774 -4681

No files matched your search

+2
View File
@@ -208,6 +208,7 @@ Colin Ian King <colin.i.king@gmail.com> <colin.king@canonical.com>
Corey Minyard <minyard@acm.org>
Damian Hobson-Garcia <dhobsong@igel.co.jp>
Dan Carpenter <error27@gmail.com> <dan.carpenter@oracle.com>
Dan Williams <djbw@kernel.org> <dan.j.williams@intel.com>
Daniel Borkmann <daniel@iogearbox.net> <danborkmann@googlemail.com>
Daniel Borkmann <daniel@iogearbox.net> <danborkmann@iogearbox.net>
Daniel Borkmann <daniel@iogearbox.net> <daniel.borkmann@tik.ee.ethz.ch>
@@ -427,6 +428,7 @@ John Stultz <johnstul@us.ibm.com>
<jon.toppins+linux@gmail.com> <jtoppins@cumulusnetworks.com>
<jon.toppins+linux@gmail.com> <jtoppins@redhat.com>
Jonas Gorski <jonas.gorski@gmail.com> <jogo@openwrt.org>
Jonathan Cameron <jic23@kernel.org> <jonathan.cameron@huawei.com>
Jordan Crouse <jordan@cosmicpenguin.net> <jcrouse@codeaurora.org>
<josh@joshtriplett.org> <josh@freedesktop.org>
<josh@joshtriplett.org> <josh@kernel.org>
+2 -7
View File
@@ -71,11 +71,6 @@ D: dosfs, LILO, some fd features, ATM, various other hacks here and there
S: Buenos Aires
S: Argentina
NTFS FILESYSTEM
N: Anton Altaparmakov
E: anton@tuxera.com
D: NTFS filesystem
N: Tim Alpaerts
E: tim_alpaerts@toyota-motor-europe.com
D: 802.2 class II logical link control layer,
@@ -85,8 +80,8 @@ S: B-2610 Wilrijk-Antwerpen
S: Belgium
N: Anton Altaparmakov
E: aia21@cantab.net
W: http://www-stu.christs.cam.ac.uk/~aia21/
E: anton@tuxera.com
W: http://www.tuxera.com/
D: Author of new NTFS driver, various other kernel hacks.
S: Christ's College
S: Cambridge CB2 3BU
+13
View File
@@ -508,6 +508,19 @@ Description:
(RO) The size of extended linear cache, if there is an extended
linear cache. Otherwise the attribute will not be visible.
What: /sys/bus/cxl/devices/regionZ/locked
Date: Mar, 2026
KernelVersion: v7.1
Contact: linux-cxl@vger.kernel.org
Description:
(RO) The CXL driver has the capability to lock a region based on
a BIOS or platform dependent configuration. Regions created as
locked are never permitted to be destroyed. Resets to participating
decoders will not result in a region destroy and will not free the
decoder resources.
What: /sys/bus/cxl/devices/regionZ/mode
Date: January, 2023
KernelVersion: v6.3
@@ -1,20 +0,0 @@
i2c Controller on XScale platforms such as IOP3xx and IXP4xx
Required properties:
- compatible : Must be one of
"intel,iop3xx-i2c"
"intel,ixp4xx-i2c";
- reg
- #address-cells = <1>;
- #size-cells = <0>;
Optional properties:
- Child nodes conforming to i2c bus binding
Example:
i2c@c8011000 {
compatible = "intel,ixp4xx-i2c";
reg = <0xc8011000 0x18>;
interrupts = <33 IRQ_TYPE_LEVEL_LOW>;
};
@@ -0,0 +1,41 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/i2c/intel,ixp4xx-i2c.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: I2c Controller on XScale platforms such as IOP3xx and IXP4xx
maintainers:
- Andi Shyti <andi.shyti@kernel.org>
allOf:
- $ref: /schemas/i2c/i2c-controller.yaml#
properties:
compatible:
enum:
- intel,iop3xx-i2c
- intel,ixp4xx-i2c
reg:
maxItems: 1
interrupts:
maxItems: 1
required:
- compatible
- reg
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
i2c@c8011000 {
compatible = "intel,ixp4xx-i2c";
reg = <0xc8011000 0x18>;
interrupts = <33 IRQ_TYPE_LEVEL_LOW>;
};
@@ -27,6 +27,7 @@ properties:
- items:
- enum:
- qcom,kaanapali-cci
- qcom,milos-cci
- qcom,qcm2290-cci
- qcom,qcs8300-cci
- qcom,sa8775p-cci
@@ -34,6 +35,7 @@ properties:
- qcom,sc8280xp-cci
- qcom,sdm670-cci
- qcom,sdm845-cci
- qcom,sm6150-cci
- qcom,sm6350-cci
- qcom,sm8250-cci
- qcom,sm8450-cci
@@ -251,6 +253,7 @@ allOf:
contains:
enum:
- qcom,sa8775p-cci
- qcom,sm6150-cci
- qcom,sm8550-cci
- qcom,sm8650-cci
- qcom,x1e80100-cci
@@ -265,6 +268,23 @@ allOf:
- const: cpas_ahb
- const: cci
- if:
properties:
compatible:
contains:
enum:
- qcom,milos-cci
then:
properties:
clocks:
minItems: 3
maxItems: 3
clock-names:
items:
- const: soc_ahb
- const: cpas_ahb
- const: cci
additionalProperties: false
examples:
@@ -15,6 +15,8 @@ description:
assigned to either I2C controller.
RTL9310 SoCs have equal capabilities but support 12 common SDA lines which
can be assigned to either I2C controller.
RTL9607C SoCs have equal capabilities but each controller only supports 1
SCL/SDA line.
properties:
compatible:
@@ -34,6 +36,7 @@ properties:
- enum:
- realtek,rtl9301-i2c
- realtek,rtl9310-i2c
- realtek,rtl9607-i2c
reg:
items:
@@ -51,6 +54,9 @@ properties:
The SCL line number of this I2C controller.
enum: [ 0, 1 ]
clocks:
maxItems: 1
patternProperties:
'^i2c@[0-9ab]$':
$ref: /schemas/i2c/i2c-controller.yaml
@@ -81,6 +87,15 @@ allOf:
then:
patternProperties:
'^i2c@[89ab]$': false
- if:
properties:
compatible:
contains:
const: realtek,rtl9607-i2c
then:
required:
- realtek,scl
- clocks
required:
- compatible
@@ -25,6 +25,7 @@ properties:
- items:
- enum:
- renesas,riic-r9a08g045 # RZ/G3S
- renesas,riic-r9a08g046 # RZ/G3L
- renesas,riic-r9a09g047 # RZ/G3E
- renesas,riic-r9a09g056 # RZ/V2N
- const: renesas,riic-r9a09g057 # RZ/V2H(P)
@@ -32,8 +32,6 @@ properties:
- const: renesas,r9a06g032-i2c # RZ/N1D
- const: renesas,rzn1-i2c # RZ/N1
- const: snps,designware-i2c
- description: Baikal-T1 SoC System I2C controller
const: baikal,bt1-sys-i2c
- description: Mobileye EyeQ DesignWare I2C controller
items:
- enum:
@@ -14,7 +14,11 @@ allOf:
properties:
compatible:
const: spacemit,k1-i2c
oneOf:
- items:
- const: spacemit,k3-i2c
- const: spacemit,k1-i2c
- const: spacemit,k1-i2c
reg:
maxItems: 1
@@ -101,7 +101,7 @@ required:
unevaluatedProperties: false
allOf:
- $ref: nand-controller.yaml
- $ref: nand-controller-legacy.yaml
- if:
properties:
@@ -10,22 +10,43 @@ maintainers:
- Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
allOf:
- $ref: nand-controller.yaml
- $ref: nand-controller-legacy.yaml
properties:
compatible:
oneOf:
- const: fsl,imx27-nand
- enum:
- fsl,imx25-nand
- fsl,imx27-nand
- fsl,imx51-nand
- fsl,imx53-nand
- items:
- enum:
- fsl,imx35-nand
- const: fsl,imx25-nand
- items:
- enum:
- fsl,imx31-nand
- const: fsl,imx27-nand
reg:
maxItems: 1
minItems: 1
items:
- description: IP register space
- description: Nand flash internal buffer space
interrupts:
maxItems: 1
clocks:
maxItems: 1
dmas:
maxItems: 1
dma-names:
items:
- const: rx-tx
required:
- compatible
- reg
@@ -11,6 +11,7 @@ maintainers:
allOf:
- $ref: mtd.yaml#
- $ref: nand-property.yaml
description: |
This file covers the generic description of a NAND chip. It implies that the
@@ -22,51 +23,6 @@ properties:
description:
Contains the chip-select IDs.
nand-ecc-engine:
description: |
A phandle on the hardware ECC engine if any. There are
basically three possibilities:
1/ The ECC engine is part of the NAND controller, in this
case the phandle should reference the parent node.
2/ The ECC engine is part of the NAND part (on-die), in this
case the phandle should reference the node itself.
3/ The ECC engine is external, in this case the phandle should
reference the specific ECC engine node.
$ref: /schemas/types.yaml#/definitions/phandle
nand-use-soft-ecc-engine:
description: Use a software ECC engine.
type: boolean
nand-no-ecc-engine:
description: Do not use any ECC correction.
type: boolean
nand-ecc-algo:
description:
Desired ECC algorithm.
$ref: /schemas/types.yaml#/definitions/string
enum: [hamming, bch, rs]
nand-ecc-strength:
description:
Maximum number of bits that can be corrected per ECC step.
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 1
nand-ecc-step-size:
description:
Number of data bytes covered by a single ECC step.
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 1
secure-regions:
description:
Regions in the NAND chip which are protected using a secure element
like Trustzone. This property contains the start address and size of
the secure regions present.
$ref: /schemas/types.yaml#/definitions/uint64-matrix
required:
- reg
@@ -0,0 +1,65 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/mtd/nand-controller-legacy.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NAND Controller Common Properties
maintainers:
- Miquel Raynal <miquel.raynal@bootlin.com>
- Richard Weinberger <richard@nod.at>
description: >
The NAND controller should be represented with its own DT node, and
all NAND chips attached to this controller should be defined as
children nodes of the NAND controller. This representation should be
enforced even for simple controllers supporting only one chip.
This is only for legacy nand controller, new controller should use
nand-controller.yaml
properties:
"#address-cells":
const: 1
"#size-cells":
enum: [0, 1]
ranges: true
cs-gpios:
description:
Array of chip-select available to the controller. The first
entries are a 1:1 mapping of the available chip-select on the
NAND controller (even if they are not used). As many additional
chip-select as needed may follow and should be phandles of GPIO
lines. 'reg' entries of the NAND chip subnodes become indexes of
this array when this property is present.
minItems: 1
maxItems: 8
partitions:
type: object
required:
- compatible
patternProperties:
"^nand@[a-f0-9]$":
type: object
$ref: raw-nand-chip.yaml#
"^partition@[0-9a-f]+$":
type: object
$ref: /schemas/mtd/partitions/partition.yaml#/$defs/partition-node
deprecated: true
allOf:
- $ref: raw-nand-property.yaml#
- $ref: nand-property.yaml#
# This is a generic file other binding inherit from and extend
additionalProperties: true
@@ -16,6 +16,8 @@ description: |
children nodes of the NAND controller. This representation should be
enforced even for simple controllers supporting only one chip.
select: false
properties:
$nodename:
pattern: "^nand-controller(@.*)?"
@@ -0,0 +1,64 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/mtd/nand-property.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NAND Chip Common Properties
maintainers:
- Miquel Raynal <miquel.raynal@bootlin.com>
description: |
This file covers the generic properties of a NAND chip. It implies that the
bus interface should not be taken into account: both raw NAND devices and
SPI-NAND devices are concerned by this description.
properties:
nand-ecc-engine:
description: |
A phandle on the hardware ECC engine if any. There are
basically three possibilities:
1/ The ECC engine is part of the NAND controller, in this
case the phandle should reference the parent node.
2/ The ECC engine is part of the NAND part (on-die), in this
case the phandle should reference the node itself.
3/ The ECC engine is external, in this case the phandle should
reference the specific ECC engine node.
$ref: /schemas/types.yaml#/definitions/phandle
nand-use-soft-ecc-engine:
description: Use a software ECC engine.
type: boolean
nand-no-ecc-engine:
description: Do not use any ECC correction.
type: boolean
nand-ecc-algo:
description:
Desired ECC algorithm.
$ref: /schemas/types.yaml#/definitions/string
enum: [hamming, bch, rs]
nand-ecc-strength:
description:
Maximum number of bits that can be corrected per ECC step.
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 1
nand-ecc-step-size:
description:
Number of data bytes covered by a single ECC step.
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 1
secure-regions:
description:
Regions in the NAND chip which are protected using a secure element
like Trustzone. This property contains the start address and size of
the secure regions present.
$ref: /schemas/types.yaml#/definitions/uint64-matrix
# This file can be referenced by more specific devices (like spi-nands)
additionalProperties: true
@@ -57,6 +57,15 @@ properties:
user space from
type: boolean
part-concat-next:
description: List of phandles to MTD partitions that need be concatenated
with the current partition.
$ref: /schemas/types.yaml#/definitions/phandle-array
minItems: 1
maxItems: 16
items:
maxItems: 1
align:
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 2
@@ -180,4 +189,15 @@ examples:
reg = <0x200000 0x100000>;
align = <0x4000>;
};
part0: partition@400000 {
part-concat-next = <&part1>;
label = "part0_0";
reg = <0x400000 0x100000>;
};
part1: partition@800000 {
label = "part0_1";
reg = <0x800000 0x800000>;
};
};
@@ -11,6 +11,7 @@ maintainers:
allOf:
- $ref: nand-chip.yaml#
- $ref: raw-nand-property.yaml#
description: |
The ECC strength and ECC step size properties define the user
@@ -31,79 +32,6 @@ properties:
description:
Contains the chip-select IDs.
nand-ecc-placement:
description:
Location of the ECC bytes. This location is unknown by default
but can be explicitly set to "oob", if all ECC bytes are
known to be stored in the OOB area, or "interleaved" if ECC
bytes will be interleaved with regular data in the main area.
$ref: /schemas/types.yaml#/definitions/string
enum: [ oob, interleaved ]
deprecated: true
nand-ecc-mode:
description:
Legacy ECC configuration mixing the ECC engine choice and
configuration.
$ref: /schemas/types.yaml#/definitions/string
enum: [none, soft, soft_bch, hw, hw_syndrome, on-die]
deprecated: true
nand-bus-width:
description:
Bus width to the NAND chip
$ref: /schemas/types.yaml#/definitions/uint32
enum: [8, 16]
default: 8
nand-on-flash-bbt:
description:
With this property, the OS will search the device for a Bad
Block Table (BBT). If not found, it will create one, reserve
a few blocks at the end of the device to store it and update
it as the device ages. Otherwise, the out-of-band area of a
few pages of all the blocks will be scanned at boot time to
find Bad Block Markers (BBM). These markers will help to
build a volatile BBT in RAM.
$ref: /schemas/types.yaml#/definitions/flag
nand-ecc-maximize:
description:
Whether or not the ECC strength should be maximized. The
maximum ECC strength is both controller and chip
dependent. The ECC engine has to select the ECC config
providing the best strength and taking the OOB area size
constraint into account. This is particularly useful when
only the in-band area is used by the upper layers, and you
want to make your NAND as reliable as possible.
$ref: /schemas/types.yaml#/definitions/flag
nand-is-boot-medium:
description:
Whether or not the NAND chip is a boot medium. Drivers might
use this information to select ECC algorithms supported by
the boot ROM or similar restrictions.
$ref: /schemas/types.yaml#/definitions/flag
nand-rb:
description:
Contains the native Ready/Busy IDs.
$ref: /schemas/types.yaml#/definitions/uint32-array
rb-gpios:
description:
Contains one or more GPIO descriptor (the numper of descriptor
depends on the number of R/B pins exposed by the flash) for the
Ready/Busy pins. Active state refers to the NAND ready state and
should be set to GPIOD_ACTIVE_HIGH unless the signal is inverted.
wp-gpios:
description:
Contains one GPIO descriptor for the Write Protect pin.
Active state refers to the NAND Write Protect state and should be
set to GPIOD_ACTIVE_LOW unless the signal is inverted.
maxItems: 1
required:
- reg
@@ -0,0 +1,98 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/mtd/raw-nand-property.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Raw NAND Chip Common Properties
maintainers:
- Miquel Raynal <miquel.raynal@bootlin.com>
description: |
The ECC strength and ECC step size properties define the user
desires in terms of correction capability of a controller. Together,
they request the ECC engine to correct {strength} bit errors per
{size} bytes for a particular raw NAND chip.
The interpretation of these parameters is implementation-defined, so
not all implementations must support all possible
combinations. However, implementations are encouraged to further
specify the value(s) they support.
properties:
nand-ecc-placement:
description:
Location of the ECC bytes. This location is unknown by default
but can be explicitly set to "oob", if all ECC bytes are
known to be stored in the OOB area, or "interleaved" if ECC
bytes will be interleaved with regular data in the main area.
$ref: /schemas/types.yaml#/definitions/string
enum: [ oob, interleaved ]
deprecated: true
nand-ecc-mode:
description:
Legacy ECC configuration mixing the ECC engine choice and
configuration.
$ref: /schemas/types.yaml#/definitions/string
enum: [none, soft, soft_bch, hw, hw_syndrome, on-die]
deprecated: true
nand-bus-width:
description:
Bus width to the NAND chip
$ref: /schemas/types.yaml#/definitions/uint32
enum: [8, 16]
default: 8
nand-on-flash-bbt:
description:
With this property, the OS will search the device for a Bad
Block Table (BBT). If not found, it will create one, reserve
a few blocks at the end of the device to store it and update
it as the device ages. Otherwise, the out-of-band area of a
few pages of all the blocks will be scanned at boot time to
find Bad Block Markers (BBM). These markers will help to
build a volatile BBT in RAM.
$ref: /schemas/types.yaml#/definitions/flag
nand-ecc-maximize:
description:
Whether or not the ECC strength should be maximized. The
maximum ECC strength is both controller and chip
dependent. The ECC engine has to select the ECC config
providing the best strength and taking the OOB area size
constraint into account. This is particularly useful when
only the in-band area is used by the upper layers, and you
want to make your NAND as reliable as possible.
$ref: /schemas/types.yaml#/definitions/flag
nand-is-boot-medium:
description:
Whether or not the NAND chip is a boot medium. Drivers might
use this information to select ECC algorithms supported by
the boot ROM or similar restrictions.
$ref: /schemas/types.yaml#/definitions/flag
nand-rb:
description:
Contains the native Ready/Busy IDs.
$ref: /schemas/types.yaml#/definitions/uint32-array
rb-gpios:
description:
Contains one or more GPIO descriptor (the numper of descriptor
depends on the number of R/B pins exposed by the flash) for the
Ready/Busy pins. Active state refers to the NAND ready state and
should be set to GPIOD_ACTIVE_HIGH unless the signal is inverted.
wp-gpios:
description:
Contains one GPIO descriptor for the Write Protect pin.
Active state refers to the NAND Write Protect state and should be
set to GPIOD_ACTIVE_LOW unless the signal is inverted.
maxItems: 1
# This is a generic file other binding inherit from and extend
additionalProperties: true
+1
View File
@@ -99,6 +99,7 @@ Documentation for filesystem implementations.
isofs
nilfs2
nfs/index
ntfs
ntfs3
ocfs2
ocfs2-online-filecheck
+159
View File
@@ -0,0 +1,159 @@
.. SPDX-License-Identifier: GPL-2.0
=================================
The Linux NTFS filesystem driver
=================================
.. Table of contents
- Overview
- Utilities support
- Supported mount options
Overview
========
NTFS is a Linux kernel filesystem driver that provides full read and write
support for NTFS volumes. It is designed for high performance, modern
kernel infrastructure (iomap, folio), and stable long-term maintenance.
Utilities support
=================
The NTFS utilities project, called ntfsprogs-plus, provides mkfs.ntfs,
fsck.ntfs, and other related tools (e.g., ntfsinfo, ntfsclone, etc.) for
creating, checking, and managing NTFS volumes. These utilities can be used
for filesystem testing with xfstests as well as for recovering corrupted
NTFS devices.
The project is available at:
https://github.com/ntfsprogs-plus/ntfsprogs-plus
Supported mount options
=======================
The NTFS driver supports the following mount options:
======================= ====================================================
iocharset=name Character set to use for converting between
the encoding is used for user visible filename and
16 bit Unicode characters.
nls=name Deprecated option. Still supported but please use
iocharset=name in the future.
uid=
gid=
umask= Provide default owner, group, and access mode mask.
These options work as documented in mount(8). By
default, the files/directories are owned by root
and he/she has read and write permissions, as well
as browse permission for directories. No one else
has any access permissions. I.e. the mode on all
files is by default rw------- and
for directories rwx------, a consequence of
the default fmask=0177 and dmask=0077.
Using a umask of zero will grant all permissions to
everyone, i.e. all files and directories will have
mode rwxrwxrwx.
fmask=
dmask= Instead of specifying umask which applies both to
files and directories, fmask applies only to files
and dmask only to directories.
showmeta=<BOOL>
show_sys_files=<BOOL> If show_sys_files is specified, show the system
files in directory listings. Otherwise the default
behaviour is to hide the system files.
Note that even when show_sys_files is specified,
"$MFT" will not be visible due to bugs/mis-features
in glibc. Further, note that irrespective of
show_sys_files, all files are accessible by name,
i.e. you can always do "ls -l \$UpCase" for example
to specifically show the system file containing
the Unicode upcase table.
case_sensitive=<BOOL> If case_sensitive is specified, treat all filenames
as case sensitive and create file names in
the POSIX namespace (default behavior). Note,
the Linux NTFS driver will never create short
filenames and will remove them on rename/delete of
the corresponding long file name. Note that files
remain accessible via their short file name, if it
exists.
nocase=<BOOL> If nocase is specified, treat filenames
case-insensitively.
disable_sparse=<BOOL> If disable_sparse is specified, creation of sparse
regions, i.e. holes, inside files is disabled for
the volume (for the duration of this mount only).
By default, creation of sparse regions is enabled,
which is consistent with the behaviour of
traditional Unix filesystems.
errors=opt Specify NTFS behavior on critical errors: panic,
remount the partition in read-only mode or
continue without doing anything (default behavior).
mft_zone_multiplier= Set the MFT zone multiplier for the volume (this
setting is not persistent across mounts and can be
changed from mount to mount but cannot be changed
on remount). Values of 1 to 4 are allowed, 1 being
the default. The MFT zone multiplier determines
how much space is reserved for the MFT on the
volume. If all other space is used up, then the
MFT zone will be shrunk dynamically, so this has no
impact on the amount of free space. However, it
can have an impact on performance by affecting
fragmentation of the MFT. In general use the
default. If you have a lot of small files then use
a higher value. The values have the following
meaning:
===== =================================
Value MFT zone size (% of volume size)
===== =================================
1 12.5%
2 25%
3 37.5%
4 50%
===== =================================
Note this option is irrelevant for read-only mount.
preallocated_size= Set preallocated size to optimize runlist merge
overhead with small chunck size.(64KB size by
default)
acl=<BOOL> Enable POSIX ACL support. When specified, POSIX
ACLs stored in extended attributes are enforced.
Default is off. Requires kernel config
NTFS_FS_POSIX_ACL enabled.
sys_immutable=<BOOL> Make NTFS system files (e.g. $MFT, $LogFile,
$Bitmap, $UpCase, etc.) immutable to user initiated
modifications for extra safety. Default is off.
nohidden=<BOOL> Hide files and directories marked with the Windows
"hidden" attribute. By default hidden items are
shown.
hide_dot_files=<BOOL> Hide names beginning with a dot ("."). By default
dot files are shown. When enabled, files and
directories created with a leading '.' will be
hidden from directory listings.
windows_names=<BOOL> Refuse creation/rename of files with characters or
reserved device names disallowed on Windows (e.g.
CON, NUL, AUX, COM1, LPT1, etc.). Default is off.
discard=<BOOL> Issue block device discard for clusters freed on
file deletion/truncation to inform underlying
storage.
======================= ====================================================
+22 -13
View File
@@ -4055,7 +4055,7 @@ S: Maintained
F: crypto/rsa*
ASYNCHRONOUS TRANSFERS/TRANSFORMS (IOAT) API
R: Dan Williams <dan.j.williams@intel.com>
R: Dan Williams <djbw@kernel.org>
S: Odd fixes
W: http://sourceforge.net/projects/xscaleiop
F: Documentation/crypto/async-tx-api.rst
@@ -6422,12 +6422,12 @@ F: include/linux/compiler_attributes.h
COMPUTE EXPRESS LINK (CXL)
M: Davidlohr Bueso <dave@stgolabs.net>
M: Jonathan Cameron <jonathan.cameron@huawei.com>
M: Jonathan Cameron <jic23@kernel.org>
M: Dave Jiang <dave.jiang@intel.com>
M: Alison Schofield <alison.schofield@intel.com>
M: Vishal Verma <vishal.l.verma@intel.com>
M: Ira Weiny <ira.weiny@intel.com>
M: Dan Williams <dan.j.williams@intel.com>
M: Dan Williams <djbw@kernel.org>
L: linux-cxl@vger.kernel.org
S: Maintained
F: Documentation/driver-api/cxl
@@ -6438,7 +6438,7 @@ F: include/uapi/linux/cxl_mem.h
F: tools/testing/cxl/
COMPUTE EXPRESS LINK PMU (CPMU)
M: Jonathan Cameron <jonathan.cameron@huawei.com>
M: Jonathan Cameron <jic23@kernel.org>
L: linux-cxl@vger.kernel.org
S: Maintained
F: Documentation/admin-guide/perf/cxl.rst
@@ -7295,7 +7295,7 @@ S: Maintained
F: scripts/dev-needs.sh
DEVICE DIRECT ACCESS (DAX)
M: Dan Williams <dan.j.williams@intel.com>
M: Dan Williams <djbw@kernel.org>
M: Vishal Verma <vishal.l.verma@intel.com>
M: Dave Jiang <dave.jiang@intel.com>
L: nvdimm@lists.linux.dev
@@ -9852,7 +9852,7 @@ F: include/linux/fcntl.h
F: include/uapi/linux/fcntl.h
FILESYSTEM DIRECT ACCESS (DAX)
M: Dan Williams <dan.j.williams@intel.com>
M: Dan Williams <djbw@kernel.org>
R: Matthew Wilcox <willy@infradead.org>
R: Jan Kara <jack@suse.cz>
L: linux-fsdevel@vger.kernel.org
@@ -10597,7 +10597,7 @@ FWCTL SUBSYSTEM
M: Dave Jiang <dave.jiang@intel.com>
M: Jason Gunthorpe <jgg@nvidia.com>
M: Saeed Mahameed <saeedm@nvidia.com>
R: Jonathan Cameron <Jonathan.Cameron@huawei.com>
R: Jonathan Cameron <jic23@kernel.org>
S: Maintained
F: Documentation/userspace-api/fwctl/
F: drivers/fwctl/
@@ -12946,7 +12946,7 @@ F: drivers/platform/x86/intel/hid.c
INTEL I/OAT DMA DRIVER
M: Dave Jiang <dave.jiang@intel.com>
R: Dan Williams <dan.j.williams@intel.com>
R: Dan Williams <djbw@kernel.org>
L: dmaengine@vger.kernel.org
S: Supported
Q: https://patchwork.kernel.org/project/linux-dmaengine/list/
@@ -14665,7 +14665,7 @@ K: libie
LIBNVDIMM BTT: BLOCK TRANSLATION TABLE
M: Vishal Verma <vishal.l.verma@intel.com>
M: Dan Williams <dan.j.williams@intel.com>
M: Dan Williams <djbw@kernel.org>
M: Dave Jiang <dave.jiang@intel.com>
L: nvdimm@lists.linux.dev
S: Supported
@@ -14674,7 +14674,7 @@ P: Documentation/nvdimm/maintainer-entry-profile.rst
F: drivers/nvdimm/btt*
LIBNVDIMM PMEM: PERSISTENT MEMORY DRIVER
M: Dan Williams <dan.j.williams@intel.com>
M: Dan Williams <djbw@kernel.org>
M: Vishal Verma <vishal.l.verma@intel.com>
M: Dave Jiang <dave.jiang@intel.com>
L: nvdimm@lists.linux.dev
@@ -14692,7 +14692,7 @@ F: Documentation/devicetree/bindings/pmem/pmem-region.yaml
F: drivers/nvdimm/of_pmem.c
LIBNVDIMM: NON-VOLATILE MEMORY DEVICE SUBSYSTEM
M: Dan Williams <dan.j.williams@intel.com>
M: Dan Williams <djbw@kernel.org>
M: Vishal Verma <vishal.l.verma@intel.com>
M: Dave Jiang <dave.jiang@intel.com>
M: Ira Weiny <ira.weiny@intel.com>
@@ -19005,6 +19005,15 @@ W: https://github.com/davejiang/linux/wiki
T: git https://github.com/davejiang/linux.git
F: drivers/ntb/hw/intel/
NTFS FILESYSTEM
M: Namjae Jeon <linkinjeon@kernel.org>
M: Hyunchul Lee <hyc.lee@gmail.com>
L: linux-fsdevel@vger.kernel.org
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/ntfs.git
F: Documentation/filesystems/ntfs.rst
F: fs/ntfs/
NTFS3 FILESYSTEM
M: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
L: ntfs3@lists.linux.dev
@@ -25369,7 +25378,7 @@ F: drivers/staging/
STANDALONE CACHE CONTROLLER DRIVERS
M: Conor Dooley <conor@kernel.org>
M: Jonathan Cameron <jonathan.cameron@huawei.com>
M: Jonathan Cameron <jic23@kernel.org>
S: Maintained
T: git https://git.kernel.org/pub/scm/linux/kernel/git/conor/linux.git/
F: Documentation/devicetree/bindings/cache/
@@ -27096,7 +27105,7 @@ S: Maintained
F: Documentation/devicetree/bindings/trigger-source/*
TRUSTED EXECUTION ENVIRONMENT SECURITY MANAGER (TSM)
M: Dan Williams <dan.j.williams@intel.com>
M: Dan Williams <djbw@kernel.org>
L: linux-coco@lists.linux.dev
S: Maintained
F: Documentation/ABI/testing/configfs-tsm-report
+15 -26
View File
@@ -79,7 +79,6 @@ struct arc_jit_data {
* The JIT pertinent context that is used by different functions.
*
* prog: The current eBPF program being handled.
* orig_prog: The original eBPF program before any possible change.
* jit: The JIT buffer and its length.
* bpf_header: The JITed program header. "jit.buf" points inside it.
* emit: If set, opcodes are written to memory; else, a dry-run.
@@ -94,12 +93,10 @@ struct arc_jit_data {
* need_extra_pass: A forecast if an "extra_pass" will occur.
* is_extra_pass: Indicates if the current pass is an extra pass.
* user_bpf_prog: True, if VM opcodes come from a real program.
* blinded: True if "constant blinding" step returned a new "prog".
* success: Indicates if the whole JIT went OK.
*/
struct jit_context {
struct bpf_prog *prog;
struct bpf_prog *orig_prog;
struct jit_buffer jit;
struct bpf_binary_header *bpf_header;
bool emit;
@@ -114,7 +111,6 @@ struct jit_context {
bool need_extra_pass;
bool is_extra_pass;
bool user_bpf_prog;
bool blinded;
bool success;
};
@@ -161,13 +157,7 @@ static int jit_ctx_init(struct jit_context *ctx, struct bpf_prog *prog)
{
memset(ctx, 0, sizeof(*ctx));
ctx->orig_prog = prog;
/* If constant blinding was requested but failed, scram. */
ctx->prog = bpf_jit_blind_constants(prog);
if (IS_ERR(ctx->prog))
return PTR_ERR(ctx->prog);
ctx->blinded = (ctx->prog != ctx->orig_prog);
ctx->prog = prog;
/* If the verifier doesn't zero-extend, then we have to do it. */
ctx->do_zext = !ctx->prog->aux->verifier_zext;
@@ -214,14 +204,6 @@ static inline void maybe_free(struct jit_context *ctx, void **mem)
*/
static void jit_ctx_cleanup(struct jit_context *ctx)
{
if (ctx->blinded) {
/* if all went well, release the orig_prog. */
if (ctx->success)
bpf_jit_prog_release_other(ctx->prog, ctx->orig_prog);
else
bpf_jit_prog_release_other(ctx->orig_prog, ctx->prog);
}
maybe_free(ctx, (void **)&ctx->bpf2insn);
maybe_free(ctx, (void **)&ctx->jit_data);
@@ -229,12 +211,19 @@ static void jit_ctx_cleanup(struct jit_context *ctx)
ctx->bpf2insn_valid = false;
/* Freeing "bpf_header" is enough. "jit.buf" is a sub-array of it. */
if (!ctx->success && ctx->bpf_header) {
bpf_jit_binary_free(ctx->bpf_header);
ctx->bpf_header = NULL;
ctx->jit.buf = NULL;
ctx->jit.index = 0;
ctx->jit.len = 0;
if (!ctx->success) {
if (ctx->bpf_header) {
bpf_jit_binary_free(ctx->bpf_header);
ctx->bpf_header = NULL;
ctx->jit.buf = NULL;
ctx->jit.index = 0;
ctx->jit.len = 0;
}
if (ctx->is_extra_pass) {
ctx->prog->bpf_func = NULL;
ctx->prog->jited = 0;
ctx->prog->jited_len = 0;
}
}
ctx->emit = false;
@@ -1411,7 +1400,7 @@ static struct bpf_prog *do_extra_pass(struct bpf_prog *prog)
* (re)locations involved that their addresses are not known
* during the first run.
*/
struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog)
{
vm_dump(prog);
+14 -35
View File
@@ -1852,6 +1852,9 @@ exit:
{
u64 val = (u32)imm | (u64)insn[1].imm << 32;
if (insn->src_reg == BPF_PSEUDO_FUNC)
goto notyet;
emit_a32_mov_i64(dst, val, ctx);
return 1;
@@ -2055,6 +2058,9 @@ go_jmp:
const s8 *r5 = bpf2a32[BPF_REG_5];
const u32 func = (u32)__bpf_call_base + (u32)imm;
if (insn->src_reg == BPF_PSEUDO_CALL)
goto notyet;
emit_a32_mov_r64(true, r0, r1, ctx);
emit_a32_mov_r64(true, r1, r2, ctx);
emit_push_r64(r5, ctx);
@@ -2142,11 +2148,9 @@ bool bpf_jit_needs_zext(void)
return true;
}
struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog)
{
struct bpf_prog *tmp, *orig_prog = prog;
struct bpf_binary_header *header;
bool tmp_blinded = false;
struct jit_ctx ctx;
unsigned int tmp_idx;
unsigned int image_size;
@@ -2156,20 +2160,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
* the interpreter.
*/
if (!prog->jit_requested)
return orig_prog;
/* If constant blinding was enabled and we failed during blinding
* then we must fall back to the interpreter. Otherwise, we save
* the new JITed code.
*/
tmp = bpf_jit_blind_constants(prog);
if (IS_ERR(tmp))
return orig_prog;
if (tmp != prog) {
tmp_blinded = true;
prog = tmp;
}
return prog;
memset(&ctx, 0, sizeof(ctx));
ctx.prog = prog;
@@ -2179,10 +2170,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
* we must fall back to the interpreter
*/
ctx.offsets = kcalloc(prog->len, sizeof(int), GFP_KERNEL);
if (ctx.offsets == NULL) {
prog = orig_prog;
goto out;
}
if (ctx.offsets == NULL)
return prog;
/* 1) fake pass to find in the length of the JITed code,
* to compute ctx->offsets and other context variables
@@ -2194,10 +2183,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
* being successful in the second pass, so just fall back
* to the interpreter.
*/
if (build_body(&ctx)) {
prog = orig_prog;
if (build_body(&ctx))
goto out_off;
}
tmp_idx = ctx.idx;
build_prologue(&ctx);
@@ -2213,10 +2200,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
ctx.idx += ctx.imm_count;
if (ctx.imm_count) {
ctx.imms = kcalloc(ctx.imm_count, sizeof(u32), GFP_KERNEL);
if (ctx.imms == NULL) {
prog = orig_prog;
if (ctx.imms == NULL)
goto out_off;
}
}
#else
/* there's nothing about the epilogue on ARMv7 */
@@ -2238,10 +2223,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
/* Not able to allocate memory for the structure then
* we must fall back to the interpretation
*/
if (header == NULL) {
prog = orig_prog;
if (header == NULL)
goto out_imms;
}
/* 2.) Actual pass to generate final JIT code */
ctx.target = (u32 *) image_ptr;
@@ -2278,16 +2261,12 @@ out_imms:
#endif
out_off:
kfree(ctx.offsets);
out:
if (tmp_blinded)
bpf_jit_prog_release_other(prog, prog == orig_prog ?
tmp : orig_prog);
return prog;
out_free:
image_ptr = NULL;
bpf_jit_binary_free(header);
prog = orig_prog;
goto out_imms;
}
+2
View File
@@ -338,6 +338,8 @@ u32 aarch64_insn_gen_cond_branch_imm(unsigned long pc, unsigned long addr,
long offset;
offset = label_imm_common(pc, addr, SZ_1M);
if (offset >= SZ_1M)
return AARCH64_BREAK_FAULT;
insn = aarch64_insn_get_bcond_value();
-3
View File
@@ -418,9 +418,6 @@ void free_initmem(void)
WARN_ON(!IS_ALIGNED((unsigned long)lm_init_begin, PAGE_SIZE));
WARN_ON(!IS_ALIGNED((unsigned long)lm_init_end, PAGE_SIZE));
/* Delete __init region from memblock.reserved. */
memblock_free(lm_init_begin, lm_init_end - lm_init_begin);
free_reserved_area(lm_init_begin, lm_init_end,
POISON_FREE_INITMEM, "unused kernel");
/*
+31 -72
View File
@@ -18,7 +18,6 @@
#include <asm/asm-extable.h>
#include <asm/byteorder.h>
#include <asm/cacheflush.h>
#include <asm/cpufeature.h>
#include <asm/debug-monitors.h>
#include <asm/insn.h>
@@ -35,8 +34,8 @@
#define ARENA_VM_START (MAX_BPF_JIT_REG + 5)
#define check_imm(bits, imm) do { \
if ((((imm) > 0) && ((imm) >> (bits))) || \
(((imm) < 0) && (~(imm) >> (bits)))) { \
if ((((imm) > 0) && ((imm) >> ((bits) - 1))) || \
(((imm) < 0) && (~(imm) >> ((bits) - 1)))) { \
pr_info("[%2d] imm=%d(0x%x) out of range\n", \
i, imm, imm); \
return -EINVAL; \
@@ -1198,8 +1197,8 @@ static int add_exception_handler(const struct bpf_insn *insn,
* >0 - successfully JITed a 16-byte eBPF instruction.
* <0 - failed to JIT.
*/
static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx,
bool extra_pass)
static int build_insn(const struct bpf_verifier_env *env, const struct bpf_insn *insn,
struct jit_ctx *ctx, bool extra_pass)
{
const u8 code = insn->code;
u8 dst = bpf2a64[insn->dst_reg];
@@ -1224,6 +1223,9 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx,
int ret;
bool sign_extend;
if (bpf_insn_is_indirect_target(env, ctx->prog, i))
emit_bti(A64_BTI_J, ctx);
switch (code) {
/* dst = src */
case BPF_ALU | BPF_MOV | BPF_X:
@@ -1899,7 +1901,7 @@ emit_cond_jmp:
return 0;
}
static int build_body(struct jit_ctx *ctx, bool extra_pass)
static int build_body(struct bpf_verifier_env *env, struct jit_ctx *ctx, bool extra_pass)
{
const struct bpf_prog *prog = ctx->prog;
int i;
@@ -1918,7 +1920,7 @@ static int build_body(struct jit_ctx *ctx, bool extra_pass)
int ret;
ctx->offset[i] = ctx->idx;
ret = build_insn(insn, ctx, extra_pass);
ret = build_insn(env, insn, ctx, extra_pass);
if (ret > 0) {
i++;
ctx->offset[i] = ctx->idx;
@@ -1961,11 +1963,6 @@ static int validate_ctx(struct jit_ctx *ctx)
return 0;
}
static inline void bpf_flush_icache(void *start, void *end)
{
flush_icache_range((unsigned long)start, (unsigned long)end);
}
static void priv_stack_init_guard(void __percpu *priv_stack_ptr, int alloc_size)
{
int cpu, underflow_idx = (alloc_size - PRIV_STACK_GUARD_SZ) >> 3;
@@ -2006,17 +2003,15 @@ struct arm64_jit_data {
struct jit_ctx ctx;
};
struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog)
{
int image_size, prog_size, extable_size, extable_align, extable_offset;
struct bpf_prog *tmp, *orig_prog = prog;
struct bpf_binary_header *header;
struct bpf_binary_header *ro_header = NULL;
struct arm64_jit_data *jit_data;
void __percpu *priv_stack_ptr = NULL;
bool was_classic = bpf_prog_was_classic(prog);
int priv_stack_alloc_sz;
bool tmp_blinded = false;
bool extra_pass = false;
struct jit_ctx ctx;
u8 *image_ptr;
@@ -2025,26 +2020,13 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
int exentry_idx;
if (!prog->jit_requested)
return orig_prog;
tmp = bpf_jit_blind_constants(prog);
/* If blinding was requested and we failed during blinding,
* we must fall back to the interpreter.
*/
if (IS_ERR(tmp))
return orig_prog;
if (tmp != prog) {
tmp_blinded = true;
prog = tmp;
}
return prog;
jit_data = prog->aux->jit_data;
if (!jit_data) {
jit_data = kzalloc_obj(*jit_data);
if (!jit_data) {
prog = orig_prog;
goto out;
}
if (!jit_data)
return prog;
prog->aux->jit_data = jit_data;
}
priv_stack_ptr = prog->aux->priv_stack_ptr;
@@ -2056,10 +2038,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
priv_stack_alloc_sz = round_up(prog->aux->stack_depth, 16) +
2 * PRIV_STACK_GUARD_SZ;
priv_stack_ptr = __alloc_percpu_gfp(priv_stack_alloc_sz, 16, GFP_KERNEL);
if (!priv_stack_ptr) {
prog = orig_prog;
if (!priv_stack_ptr)
goto out_priv_stack;
}
priv_stack_init_guard(priv_stack_ptr, priv_stack_alloc_sz);
prog->aux->priv_stack_ptr = priv_stack_ptr;
@@ -2079,10 +2059,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
ctx.prog = prog;
ctx.offset = kvzalloc_objs(int, prog->len + 1);
if (ctx.offset == NULL) {
prog = orig_prog;
if (ctx.offset == NULL)
goto out_off;
}
ctx.user_vm_start = bpf_arena_get_user_vm_start(prog->aux->arena);
ctx.arena_vm_start = bpf_arena_get_kern_vm_start(prog->aux->arena);
@@ -2095,15 +2073,11 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
* BPF line info needs ctx->offset[i] to be the offset of
* instruction[i] in jited image, so build prologue first.
*/
if (build_prologue(&ctx, was_classic)) {
prog = orig_prog;
if (build_prologue(&ctx, was_classic))
goto out_off;
}
if (build_body(&ctx, extra_pass)) {
prog = orig_prog;
if (build_body(env, &ctx, extra_pass))
goto out_off;
}
ctx.epilogue_offset = ctx.idx;
build_epilogue(&ctx, was_classic);
@@ -2121,10 +2095,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
ro_header = bpf_jit_binary_pack_alloc(image_size, &ro_image_ptr,
sizeof(u64), &header, &image_ptr,
jit_fill_hole);
if (!ro_header) {
prog = orig_prog;
if (!ro_header)
goto out_off;
}
/* Pass 2: Determine jited position and result for each instruction */
@@ -2152,10 +2124,8 @@ skip_init_ctx:
/* Dont write body instructions to memory for now */
ctx.write = false;
if (build_body(&ctx, extra_pass)) {
prog = orig_prog;
if (build_body(env, &ctx, extra_pass))
goto out_free_hdr;
}
ctx.epilogue_offset = ctx.idx;
ctx.exentry_idx = exentry_idx;
@@ -2163,20 +2133,16 @@ skip_init_ctx:
ctx.write = true;
/* Pass 3: Adjust jump offset and write final image */
if (build_body(&ctx, extra_pass) ||
WARN_ON_ONCE(ctx.idx != ctx.epilogue_offset)) {
prog = orig_prog;
if (build_body(env, &ctx, extra_pass) ||
WARN_ON_ONCE(ctx.idx != ctx.epilogue_offset))
goto out_free_hdr;
}
build_epilogue(&ctx, was_classic);
build_plt(&ctx);
/* Extra pass to validate JITed code. */
if (validate_ctx(&ctx)) {
prog = orig_prog;
if (validate_ctx(&ctx))
goto out_free_hdr;
}
/* update the real prog size */
prog_size = sizeof(u32) * ctx.idx;
@@ -2193,23 +2159,14 @@ skip_init_ctx:
if (extra_pass && ctx.idx > jit_data->ctx.idx) {
pr_err_once("multi-func JIT bug %d > %d\n",
ctx.idx, jit_data->ctx.idx);
prog->bpf_func = NULL;
prog->jited = 0;
prog->jited_len = 0;
goto out_free_hdr;
}
if (WARN_ON(bpf_jit_binary_pack_finalize(ro_header, header))) {
/* ro_header has been freed */
/* ro_header and header has been freed */
ro_header = NULL;
prog = orig_prog;
goto out_off;
header = NULL;
goto out_free_hdr;
}
/*
* The instructions have now been copied to the ROX region from
* where they will execute. Now the data cache has to be cleaned to
* the PoU and the I-cache has to be invalidated for the VAs.
*/
bpf_flush_icache(ro_header, ctx.ro_image + ctx.idx);
} else {
jit_data->ctx = ctx;
jit_data->ro_image = ro_image_ptr;
@@ -2245,13 +2202,15 @@ out_priv_stack:
kfree(jit_data);
prog->aux->jit_data = NULL;
}
out:
if (tmp_blinded)
bpf_jit_prog_release_other(prog, prog == orig_prog ?
tmp : orig_prog);
return prog;
out_free_hdr:
if (extra_pass) {
prog->bpf_func = NULL;
prog->jited = 0;
prog->jited_len = 0;
}
if (header) {
bpf_arch_text_copy(&ro_header->size, &header->size,
sizeof(header->size));
+18 -43
View File
@@ -1920,45 +1920,28 @@ int arch_bpf_trampoline_size(const struct btf_func_model *m, u32 flags,
return ret < 0 ? ret : ret * LOONGARCH_INSN_SIZE;
}
struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog)
{
bool tmp_blinded = false, extra_pass = false;
bool extra_pass = false;
u8 *image_ptr, *ro_image_ptr;
int image_size, prog_size, extable_size;
struct jit_ctx ctx;
struct jit_data *jit_data;
struct bpf_binary_header *header;
struct bpf_binary_header *ro_header;
struct bpf_prog *tmp, *orig_prog = prog;
/*
* If BPF JIT was not enabled then we must fall back to
* the interpreter.
*/
if (!prog->jit_requested)
return orig_prog;
tmp = bpf_jit_blind_constants(prog);
/*
* If blinding was requested and we failed during blinding,
* we must fall back to the interpreter. Otherwise, we save
* the new JITed code.
*/
if (IS_ERR(tmp))
return orig_prog;
if (tmp != prog) {
tmp_blinded = true;
prog = tmp;
}
return prog;
jit_data = prog->aux->jit_data;
if (!jit_data) {
jit_data = kzalloc_obj(*jit_data);
if (!jit_data) {
prog = orig_prog;
goto out;
}
if (!jit_data)
return prog;
prog->aux->jit_data = jit_data;
}
if (jit_data->ctx.offset) {
@@ -1978,17 +1961,13 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
ctx.user_vm_start = bpf_arena_get_user_vm_start(prog->aux->arena);
ctx.offset = kvcalloc(prog->len + 1, sizeof(u32), GFP_KERNEL);
if (ctx.offset == NULL) {
prog = orig_prog;
if (ctx.offset == NULL)
goto out_offset;
}
/* 1. Initial fake pass to compute ctx->idx and set ctx->flags */
build_prologue(&ctx);
if (build_body(&ctx, extra_pass)) {
prog = orig_prog;
if (build_body(&ctx, extra_pass))
goto out_offset;
}
ctx.epilogue_offset = ctx.idx;
build_epilogue(&ctx);
@@ -2004,10 +1983,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
/* Now we know the size of the structure to make */
ro_header = bpf_jit_binary_pack_alloc(image_size, &ro_image_ptr, sizeof(u32),
&header, &image_ptr, jit_fill_hole);
if (!ro_header) {
prog = orig_prog;
if (!ro_header)
goto out_offset;
}
/* 2. Now, the actual pass to generate final JIT code */
/*
@@ -2027,17 +2004,13 @@ skip_init_ctx:
ctx.num_exentries = 0;
build_prologue(&ctx);
if (build_body(&ctx, extra_pass)) {
prog = orig_prog;
if (build_body(&ctx, extra_pass))
goto out_free;
}
build_epilogue(&ctx);
/* 3. Extra pass to validate JITed code */
if (validate_ctx(&ctx)) {
prog = orig_prog;
if (validate_ctx(&ctx))
goto out_free;
}
/* And we're done */
if (bpf_jit_enable > 1)
@@ -2050,9 +2023,9 @@ skip_init_ctx:
goto out_free;
}
if (WARN_ON(bpf_jit_binary_pack_finalize(ro_header, header))) {
/* ro_header has been freed */
/* ro_header and header have been freed */
ro_header = NULL;
prog = orig_prog;
header = NULL;
goto out_free;
}
/*
@@ -2084,13 +2057,15 @@ out_offset:
prog->aux->jit_data = NULL;
}
out:
if (tmp_blinded)
bpf_jit_prog_release_other(prog, prog == orig_prog ? tmp : orig_prog);
return prog;
out_free:
if (extra_pass) {
prog->bpf_func = NULL;
prog->jited = 0;
prog->jited_len = 0;
}
if (header) {
bpf_arch_text_copy(&ro_header->size, &header->size, sizeof(header->size));
bpf_jit_binary_pack_free(ro_header, header);
+2 -20
View File
@@ -909,12 +909,10 @@ bool bpf_jit_needs_zext(void)
return true;
}
struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog)
{
struct bpf_prog *tmp, *orig_prog = prog;
struct bpf_binary_header *header = NULL;
struct jit_context ctx;
bool tmp_blinded = false;
unsigned int tmp_idx;
unsigned int image_size;
u8 *image_ptr;
@@ -925,19 +923,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
* the interpreter.
*/
if (!prog->jit_requested)
return orig_prog;
/*
* If constant blinding was enabled and we failed during blinding
* then we must fall back to the interpreter. Otherwise, we save
* the new JITed code.
*/
tmp = bpf_jit_blind_constants(prog);
if (IS_ERR(tmp))
return orig_prog;
if (tmp != prog) {
tmp_blinded = true;
prog = tmp;
}
return prog;
memset(&ctx, 0, sizeof(ctx));
ctx.program = prog;
@@ -1025,14 +1011,10 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
prog->jited_len = image_size;
out:
if (tmp_blinded)
bpf_jit_prog_release_other(prog, prog == orig_prog ?
tmp : orig_prog);
kfree(ctx.descriptors);
return prog;
out_err:
prog = orig_prog;
if (header)
bpf_jit_binary_free(header);
goto out;
+30 -45
View File
@@ -41,33 +41,22 @@ bool bpf_jit_needs_zext(void)
return true;
}
struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog)
{
unsigned int prog_size = 0, extable_size = 0;
bool tmp_blinded = false, extra_pass = false;
struct bpf_prog *tmp, *orig_prog = prog;
bool extra_pass = false;
int pass = 0, prev_ninsns = 0, prologue_len, i;
struct hppa_jit_data *jit_data;
struct hppa_jit_context *ctx;
if (!prog->jit_requested)
return orig_prog;
tmp = bpf_jit_blind_constants(prog);
if (IS_ERR(tmp))
return orig_prog;
if (tmp != prog) {
tmp_blinded = true;
prog = tmp;
}
return prog;
jit_data = prog->aux->jit_data;
if (!jit_data) {
jit_data = kzalloc_obj(*jit_data);
if (!jit_data) {
prog = orig_prog;
goto out;
}
if (!jit_data)
return prog;
prog->aux->jit_data = jit_data;
}
@@ -81,10 +70,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
ctx->prog = prog;
ctx->offset = kzalloc_objs(int, prog->len);
if (!ctx->offset) {
prog = orig_prog;
goto out_offset;
}
if (!ctx->offset)
goto out_err;
for (i = 0; i < prog->len; i++) {
prev_ninsns += 20;
ctx->offset[i] = prev_ninsns;
@@ -93,10 +80,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
for (i = 0; i < NR_JIT_ITERATIONS; i++) {
pass++;
ctx->ninsns = 0;
if (build_body(ctx, extra_pass, ctx->offset)) {
prog = orig_prog;
goto out_offset;
}
if (build_body(ctx, extra_pass, ctx->offset))
goto out_err;
ctx->body_len = ctx->ninsns;
bpf_jit_build_prologue(ctx);
ctx->prologue_len = ctx->ninsns - ctx->body_len;
@@ -116,10 +101,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
&jit_data->image,
sizeof(long),
bpf_fill_ill_insns);
if (!jit_data->header) {
prog = orig_prog;
goto out_offset;
}
if (!jit_data->header)
goto out_err;
ctx->insns = (u32 *)jit_data->image;
/*
@@ -134,8 +117,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
pr_err("bpf-jit: image did not converge in <%d passes!\n", i);
if (jit_data->header)
bpf_jit_binary_free(jit_data->header);
prog = orig_prog;
goto out_offset;
goto out_err;
}
if (extable_size)
@@ -148,8 +130,7 @@ skip_init_ctx:
bpf_jit_build_prologue(ctx);
if (build_body(ctx, extra_pass, NULL)) {
bpf_jit_binary_free(jit_data->header);
prog = orig_prog;
goto out_offset;
goto out_err;
}
bpf_jit_build_epilogue(ctx);
@@ -160,20 +141,19 @@ skip_init_ctx:
{ extern int machine_restart(char *); machine_restart(""); }
}
if (!prog->is_func || extra_pass) {
if (bpf_jit_binary_lock_ro(jit_data->header)) {
bpf_jit_binary_free(jit_data->header);
goto out_err;
}
bpf_flush_icache(jit_data->header, ctx->insns + ctx->ninsns);
}
prog->bpf_func = (void *)ctx->insns;
prog->jited = 1;
prog->jited_len = prog_size;
bpf_flush_icache(jit_data->header, ctx->insns + ctx->ninsns);
if (!prog->is_func || extra_pass) {
if (bpf_jit_binary_lock_ro(jit_data->header)) {
bpf_jit_binary_free(jit_data->header);
prog->bpf_func = NULL;
prog->jited = 0;
prog->jited_len = 0;
goto out_offset;
}
prologue_len = ctx->epilogue_offset - ctx->body_len;
for (i = 0; i < prog->len; i++)
ctx->offset[i] += prologue_len;
@@ -183,14 +163,19 @@ out_offset:
kfree(jit_data);
prog->aux->jit_data = NULL;
}
out:
if (HPPA_JIT_REBOOT)
{ extern int machine_restart(char *); machine_restart(""); }
if (tmp_blinded)
bpf_jit_prog_release_other(prog, prog == orig_prog ?
tmp : orig_prog);
return prog;
out_err:
if (extra_pass) {
prog->bpf_func = NULL;
prog->jited = 0;
prog->jited_len = 0;
}
goto out_offset;
}
u64 hppa_div64(u64 div, u64 divisor)
+2 -14
View File
@@ -775,24 +775,12 @@ void __init fadump_update_elfcore_header(char *bufp)
static void *__init fadump_alloc_buffer(unsigned long size)
{
unsigned long count, i;
struct page *page;
void *vaddr;
vaddr = alloc_pages_exact(size, GFP_KERNEL | __GFP_ZERO);
if (!vaddr)
return NULL;
count = PAGE_ALIGN(size) / PAGE_SIZE;
page = virt_to_page(vaddr);
for (i = 0; i < count; i++)
mark_page_reserved(page + i);
return vaddr;
return alloc_pages_exact(size, GFP_KERNEL | __GFP_ZERO);
}
static void fadump_free_buffer(unsigned long vaddr, unsigned long size)
{
free_reserved_area((void *)vaddr, (void *)(vaddr + size), -1, NULL);
free_pages_exact((void *)vaddr, size);
}
s32 __init fadump_setup_cpu_notes_buf(u32 num_cpus)
+27 -47
View File
@@ -162,7 +162,7 @@ static void priv_stack_check_guard(void __percpu *priv_stack_ptr, int alloc_size
}
}
struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)
struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *fp)
{
u32 proglen;
u32 alloclen;
@@ -177,9 +177,6 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)
void __percpu *priv_stack_ptr = NULL;
struct bpf_binary_header *fhdr = NULL;
struct bpf_binary_header *hdr = NULL;
struct bpf_prog *org_fp = fp;
struct bpf_prog *tmp_fp = NULL;
bool bpf_blinded = false;
bool extra_pass = false;
u8 *fimage = NULL;
u32 *fcode_base = NULL;
@@ -187,24 +184,13 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)
u32 fixup_len;
if (!fp->jit_requested)
return org_fp;
tmp_fp = bpf_jit_blind_constants(org_fp);
if (IS_ERR(tmp_fp))
return org_fp;
if (tmp_fp != org_fp) {
bpf_blinded = true;
fp = tmp_fp;
}
return fp;
jit_data = fp->aux->jit_data;
if (!jit_data) {
jit_data = kzalloc_obj(*jit_data);
if (!jit_data) {
fp = org_fp;
goto out;
}
if (!jit_data)
return fp;
fp->aux->jit_data = jit_data;
}
@@ -219,10 +205,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)
priv_stack_alloc_size = round_up(fp->aux->stack_depth, 16) +
2 * PRIV_STACK_GUARD_SZ;
priv_stack_ptr = __alloc_percpu_gfp(priv_stack_alloc_size, 16, GFP_KERNEL);
if (!priv_stack_ptr) {
fp = org_fp;
if (!priv_stack_ptr)
goto out_priv_stack;
}
priv_stack_init_guard(priv_stack_ptr, priv_stack_alloc_size);
fp->aux->priv_stack_ptr = priv_stack_ptr;
@@ -249,10 +233,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)
}
addrs = kcalloc(flen + 1, sizeof(*addrs), GFP_KERNEL);
if (addrs == NULL) {
fp = org_fp;
goto out_addrs;
}
if (addrs == NULL)
goto out_err;
memset(&cgctx, 0, sizeof(struct codegen_context));
bpf_jit_init_reg_mapping(&cgctx);
@@ -279,11 +261,9 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)
}
/* Scouting faux-generate pass 0 */
if (bpf_jit_build_body(fp, NULL, NULL, &cgctx, addrs, 0, false)) {
if (bpf_jit_build_body(fp, NULL, NULL, &cgctx, addrs, 0, false))
/* We hit something illegal or unsupported. */
fp = org_fp;
goto out_addrs;
}
goto out_err;
/*
* If we have seen a tail call, we need a second pass.
@@ -294,10 +274,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)
*/
if (cgctx.seen & SEEN_TAILCALL || !is_offset_in_branch_range((long)cgctx.idx * 4)) {
cgctx.idx = 0;
if (bpf_jit_build_body(fp, NULL, NULL, &cgctx, addrs, 0, false)) {
fp = org_fp;
goto out_addrs;
}
if (bpf_jit_build_body(fp, NULL, NULL, &cgctx, addrs, 0, false))
goto out_err;
}
bpf_jit_realloc_regs(&cgctx);
@@ -318,10 +296,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)
fhdr = bpf_jit_binary_pack_alloc(alloclen, &fimage, 4, &hdr, &image,
bpf_jit_fill_ill_insns);
if (!fhdr) {
fp = org_fp;
goto out_addrs;
}
if (!fhdr)
goto out_err;
if (extable_len)
fp->aux->extable = (void *)fimage + FUNCTION_DESCR_SIZE + proglen + fixup_len;
@@ -340,8 +316,7 @@ skip_init_ctx:
extra_pass)) {
bpf_arch_text_copy(&fhdr->size, &hdr->size, sizeof(hdr->size));
bpf_jit_binary_pack_free(fhdr, hdr);
fp = org_fp;
goto out_addrs;
goto out_err;
}
bpf_jit_build_epilogue(code_base, &cgctx);
@@ -363,15 +338,16 @@ skip_init_ctx:
((u64 *)image)[1] = local_paca->kernel_toc;
#endif
if (!fp->is_func || extra_pass) {
if (bpf_jit_binary_pack_finalize(fhdr, hdr))
goto out_err;
}
fp->bpf_func = (void *)fimage;
fp->jited = 1;
fp->jited_len = cgctx.idx * 4 + FUNCTION_DESCR_SIZE;
if (!fp->is_func || extra_pass) {
if (bpf_jit_binary_pack_finalize(fhdr, hdr)) {
fp = org_fp;
goto out_addrs;
}
bpf_prog_fill_jited_linfo(fp, addrs);
/*
* On ABI V1, executable code starts after the function
@@ -398,11 +374,15 @@ out_priv_stack:
jit_data->hdr = hdr;
}
out:
if (bpf_blinded)
bpf_jit_prog_release_other(fp, fp == org_fp ? tmp_fp : org_fp);
return fp;
out_err:
if (extra_pass) {
fp->bpf_func = NULL;
fp->jited = 0;
fp->jited_len = 0;
}
goto out_addrs;
}
/*
+1 -10
View File
@@ -303,7 +303,6 @@ static int __init create_opalcore(void)
struct device_node *dn;
struct opalcore *new;
loff_t opalcore_off;
struct page *page;
Elf64_Phdr *phdr;
Elf64_Ehdr *elf;
int i, ret;
@@ -328,11 +327,6 @@ static int __init create_opalcore(void)
oc_conf->opalcorebuf_sz = 0;
return -ENOMEM;
}
count = oc_conf->opalcorebuf_sz / PAGE_SIZE;
page = virt_to_page(oc_conf->opalcorebuf);
for (i = 0; i < count; i++)
mark_page_reserved(page + i);
pr_debug("opalcorebuf = 0x%llx\n", (u64)oc_conf->opalcorebuf);
/* Read OPAL related device-tree entries */
@@ -437,10 +431,7 @@ static void opalcore_cleanup(void)
/* free the buffer used for setting up OPAL core */
if (oc_conf->opalcorebuf) {
void *end = (void *)((u64)oc_conf->opalcorebuf +
oc_conf->opalcorebuf_sz);
free_reserved_area(oc_conf->opalcorebuf, end, -1, NULL);
free_pages_exact(oc_conf->opalcorebuf, oc_conf->opalcorebuf_sz);
oc_conf->opalcorebuf = NULL;
oc_conf->opalcorebuf_sz = 0;
}
-6
View File
@@ -11,7 +11,6 @@
#include <linux/bpf.h>
#include <linux/filter.h>
#include <asm/cacheflush.h>
/* verify runtime detection extension status */
#define rv_ext_enabled(ext) \
@@ -105,11 +104,6 @@ static inline void bpf_fill_ill_insns(void *area, unsigned int size)
memset(area, 0, size);
}
static inline void bpf_flush_icache(void *start, void *end)
{
flush_icache_range((unsigned long)start, (unsigned long)end);
}
/* Emit a 4-byte riscv instruction. */
static inline void emit(const u32 insn, struct rv_jit_context *ctx)
{
+23 -47
View File
@@ -41,32 +41,22 @@ bool bpf_jit_needs_zext(void)
return true;
}
struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog)
{
unsigned int prog_size = 0, extable_size = 0;
bool tmp_blinded = false, extra_pass = false;
struct bpf_prog *tmp, *orig_prog = prog;
bool extra_pass = false;
int pass = 0, prev_ninsns = 0, i;
struct rv_jit_data *jit_data;
struct rv_jit_context *ctx;
if (!prog->jit_requested)
return orig_prog;
tmp = bpf_jit_blind_constants(prog);
if (IS_ERR(tmp))
return orig_prog;
if (tmp != prog) {
tmp_blinded = true;
prog = tmp;
}
return prog;
jit_data = prog->aux->jit_data;
if (!jit_data) {
jit_data = kzalloc_obj(*jit_data);
if (!jit_data) {
prog = orig_prog;
goto out;
return prog;
}
prog->aux->jit_data = jit_data;
}
@@ -83,15 +73,11 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
ctx->user_vm_start = bpf_arena_get_user_vm_start(prog->aux->arena);
ctx->prog = prog;
ctx->offset = kzalloc_objs(int, prog->len);
if (!ctx->offset) {
prog = orig_prog;
if (!ctx->offset)
goto out_offset;
}
if (build_body(ctx, extra_pass, NULL)) {
prog = orig_prog;
if (build_body(ctx, extra_pass, NULL))
goto out_offset;
}
for (i = 0; i < prog->len; i++) {
prev_ninsns += 32;
@@ -105,10 +91,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
bpf_jit_build_prologue(ctx, bpf_is_subprog(prog));
ctx->prologue_len = ctx->ninsns;
if (build_body(ctx, extra_pass, ctx->offset)) {
prog = orig_prog;
if (build_body(ctx, extra_pass, ctx->offset))
goto out_offset;
}
ctx->epilogue_offset = ctx->ninsns;
bpf_jit_build_epilogue(ctx);
@@ -126,10 +110,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
&jit_data->ro_image, sizeof(u32),
&jit_data->header, &jit_data->image,
bpf_fill_ill_insns);
if (!jit_data->ro_header) {
prog = orig_prog;
if (!jit_data->ro_header)
goto out_offset;
}
/*
* Use the image(RW) for writing the JITed instructions. But also save
@@ -150,7 +132,6 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
if (i == NR_JIT_ITERATIONS) {
pr_err("bpf-jit: image did not converge in <%d passes!\n", i);
prog = orig_prog;
goto out_free_hdr;
}
@@ -163,33 +144,27 @@ skip_init_ctx:
ctx->nexentries = 0;
bpf_jit_build_prologue(ctx, bpf_is_subprog(prog));
if (build_body(ctx, extra_pass, NULL)) {
prog = orig_prog;
if (build_body(ctx, extra_pass, NULL))
goto out_free_hdr;
}
bpf_jit_build_epilogue(ctx);
if (bpf_jit_enable > 1)
bpf_jit_dump(prog->len, prog_size, pass, ctx->insns);
if (!prog->is_func || extra_pass) {
if (WARN_ON(bpf_jit_binary_pack_finalize(jit_data->ro_header, jit_data->header))) {
/* ro_header has been freed */
jit_data->ro_header = NULL;
jit_data->header = NULL;
goto out_free_hdr;
}
}
prog->bpf_func = (void *)ctx->ro_insns + cfi_get_offset();
prog->jited = 1;
prog->jited_len = prog_size - cfi_get_offset();
if (!prog->is_func || extra_pass) {
if (WARN_ON(bpf_jit_binary_pack_finalize(jit_data->ro_header, jit_data->header))) {
/* ro_header has been freed */
jit_data->ro_header = NULL;
prog = orig_prog;
goto out_offset;
}
/*
* The instructions have now been copied to the ROX region from
* where they will execute.
* Write any modified data cache blocks out to memory and
* invalidate the corresponding blocks in the instruction cache.
*/
bpf_flush_icache(jit_data->ro_header, ctx->ro_insns + ctx->ninsns);
for (i = 0; i < prog->len; i++)
ctx->offset[i] = ninsns_rvoff(ctx->offset[i]);
bpf_prog_fill_jited_linfo(prog, ctx->offset);
@@ -198,14 +173,15 @@ out_offset:
kfree(jit_data);
prog->aux->jit_data = NULL;
}
out:
if (tmp_blinded)
bpf_jit_prog_release_other(prog, prog == orig_prog ?
tmp : orig_prog);
return prog;
out_free_hdr:
if (extra_pass) {
prog->bpf_func = NULL;
prog->jited = 0;
prog->jited_len = 0;
}
if (jit_data->header) {
bpf_arch_text_copy(&jit_data->ro_header->size, &jit_data->header->size,
sizeof(jit_data->header->size));
+21 -40
View File
@@ -2312,38 +2312,22 @@ static struct bpf_binary_header *bpf_jit_alloc(struct bpf_jit *jit,
/*
* Compile eBPF program "fp"
*/
struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)
struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *fp)
{
struct bpf_prog *tmp, *orig_fp = fp;
struct bpf_binary_header *header;
struct s390_jit_data *jit_data;
bool tmp_blinded = false;
bool extra_pass = false;
struct bpf_jit jit;
int pass;
if (!fp->jit_requested)
return orig_fp;
tmp = bpf_jit_blind_constants(fp);
/*
* If blinding was requested and we failed during blinding,
* we must fall back to the interpreter.
*/
if (IS_ERR(tmp))
return orig_fp;
if (tmp != fp) {
tmp_blinded = true;
fp = tmp;
}
return fp;
jit_data = fp->aux->jit_data;
if (!jit_data) {
jit_data = kzalloc_obj(*jit_data);
if (!jit_data) {
fp = orig_fp;
goto out;
}
if (!jit_data)
return fp;
fp->aux->jit_data = jit_data;
}
if (jit_data->ctx.addrs) {
@@ -2356,34 +2340,27 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)
memset(&jit, 0, sizeof(jit));
jit.addrs = kvcalloc(fp->len + 1, sizeof(*jit.addrs), GFP_KERNEL);
if (jit.addrs == NULL) {
fp = orig_fp;
goto free_addrs;
}
if (jit.addrs == NULL)
goto out_err;
/*
* Three initial passes:
* - 1/2: Determine clobbered registers
* - 3: Calculate program size and addrs array
*/
for (pass = 1; pass <= 3; pass++) {
if (bpf_jit_prog(&jit, fp, extra_pass)) {
fp = orig_fp;
goto free_addrs;
}
if (bpf_jit_prog(&jit, fp, extra_pass))
goto out_err;
}
/*
* Final pass: Allocate and generate program
*/
header = bpf_jit_alloc(&jit, fp);
if (!header) {
fp = orig_fp;
goto free_addrs;
}
if (!header)
goto out_err;
skip_init_ctx:
if (bpf_jit_prog(&jit, fp, extra_pass)) {
bpf_jit_binary_free(header);
fp = orig_fp;
goto free_addrs;
goto out_err;
}
if (bpf_jit_enable > 1) {
bpf_jit_dump(fp->len, jit.size, pass, jit.prg_buf);
@@ -2392,8 +2369,7 @@ skip_init_ctx:
if (!fp->is_func || extra_pass) {
if (bpf_jit_binary_lock_ro(header)) {
bpf_jit_binary_free(header);
fp = orig_fp;
goto free_addrs;
goto out_err;
}
} else {
jit_data->header = header;
@@ -2411,11 +2387,16 @@ free_addrs:
kfree(jit_data);
fp->aux->jit_data = NULL;
}
out:
if (tmp_blinded)
bpf_jit_prog_release_other(fp, fp == orig_fp ?
tmp : orig_fp);
return fp;
out_err:
if (extra_pass) {
fp->bpf_func = NULL;
fp->jited = 0;
fp->jited_len = 0;
}
goto free_addrs;
}
bool bpf_jit_supports_kfunc_call(void)
+1 -3
View File
@@ -183,14 +183,12 @@ static struct mdesc_handle * __init mdesc_memblock_alloc(unsigned int mdesc_size
static void __init mdesc_memblock_free(struct mdesc_handle *hp)
{
unsigned int alloc_size;
unsigned long start;
BUG_ON(refcount_read(&hp->refcnt) != 0);
BUG_ON(!list_empty(&hp->list));
alloc_size = PAGE_ALIGN(hp->handle_size);
start = __pa(hp);
memblock_free_late(start, alloc_size);
memblock_free(hp, alloc_size);
}
static struct mdesc_mem_ops memblock_mdesc_ops = {
+22 -41
View File
@@ -1477,39 +1477,24 @@ struct sparc64_jit_data {
struct jit_ctx ctx;
};
struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog)
{
struct bpf_prog *tmp, *orig_prog = prog;
struct sparc64_jit_data *jit_data;
struct bpf_binary_header *header;
u32 prev_image_size, image_size;
bool tmp_blinded = false;
bool extra_pass = false;
struct jit_ctx ctx;
u8 *image_ptr;
int pass, i;
if (!prog->jit_requested)
return orig_prog;
tmp = bpf_jit_blind_constants(prog);
/* If blinding was requested and we failed during blinding,
* we must fall back to the interpreter.
*/
if (IS_ERR(tmp))
return orig_prog;
if (tmp != prog) {
tmp_blinded = true;
prog = tmp;
}
return prog;
jit_data = prog->aux->jit_data;
if (!jit_data) {
jit_data = kzalloc_obj(*jit_data);
if (!jit_data) {
prog = orig_prog;
goto out;
}
if (!jit_data)
return prog;
prog->aux->jit_data = jit_data;
}
if (jit_data->ctx.offset) {
@@ -1527,10 +1512,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
ctx.prog = prog;
ctx.offset = kmalloc_array(prog->len, sizeof(unsigned int), GFP_KERNEL);
if (ctx.offset == NULL) {
prog = orig_prog;
goto out_off;
}
if (ctx.offset == NULL)
goto out_err;
/* Longest sequence emitted is for bswap32, 12 instructions. Pre-cook
* the offset array so that we converge faster.
@@ -1543,10 +1526,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
ctx.idx = 0;
build_prologue(&ctx);
if (build_body(&ctx)) {
prog = orig_prog;
goto out_off;
}
if (build_body(&ctx))
goto out_err;
build_epilogue(&ctx);
if (bpf_jit_enable > 1)
@@ -1569,10 +1550,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
image_size = sizeof(u32) * ctx.idx;
header = bpf_jit_binary_alloc(image_size, &image_ptr,
sizeof(u32), jit_fill_hole);
if (header == NULL) {
prog = orig_prog;
goto out_off;
}
if (header == NULL)
goto out_err;
ctx.image = (u32 *)image_ptr;
skip_init_ctx:
@@ -1582,8 +1561,7 @@ skip_init_ctx:
if (build_body(&ctx)) {
bpf_jit_binary_free(header);
prog = orig_prog;
goto out_off;
goto out_err;
}
build_epilogue(&ctx);
@@ -1592,8 +1570,7 @@ skip_init_ctx:
pr_err("bpf_jit: Failed to converge, prev_size=%u size=%d\n",
prev_image_size, ctx.idx * 4);
bpf_jit_binary_free(header);
prog = orig_prog;
goto out_off;
goto out_err;
}
if (bpf_jit_enable > 1)
@@ -1604,8 +1581,7 @@ skip_init_ctx:
if (!prog->is_func || extra_pass) {
if (bpf_jit_binary_lock_ro(header)) {
bpf_jit_binary_free(header);
prog = orig_prog;
goto out_off;
goto out_err;
}
} else {
jit_data->ctx = ctx;
@@ -1624,9 +1600,14 @@ out_off:
kfree(jit_data);
prog->aux->jit_data = NULL;
}
out:
if (tmp_blinded)
bpf_jit_prog_release_other(prog, prog == orig_prog ?
tmp : orig_prog);
return prog;
out_err:
if (extra_pass) {
prog->bpf_func = NULL;
prog->jited = 0;
prog->jited_len = 0;
}
goto out_off;
}
+18 -6
View File
@@ -2448,12 +2448,6 @@ void __init alternative_instructions(void)
__smp_locks, __smp_locks_end,
_text, _etext);
}
if (!uniproc_patched || num_possible_cpus() == 1) {
free_init_pages("SMP alternatives",
(unsigned long)__smp_locks,
(unsigned long)__smp_locks_end);
}
#endif
restart_nmi();
@@ -2462,6 +2456,24 @@ void __init alternative_instructions(void)
alt_reloc_selftest();
}
#ifdef CONFIG_SMP
/*
* With CONFIG_DEFERRED_STRUCT_PAGE_INIT enabled we can free_init_pages() only
* after the deferred initialization of the memory map is complete.
*/
static int __init free_smp_locks(void)
{
if (!uniproc_patched || num_possible_cpus() == 1) {
free_init_pages("SMP alternatives",
(unsigned long)__smp_locks,
(unsigned long)__smp_locks_end);
}
return 0;
}
arch_initcall(free_smp_locks);
#endif
/**
* text_poke_early - Update instructions on a live kernel at boot time
* @addr: address to modify
+1 -1
View File
@@ -426,7 +426,7 @@ int __init ima_free_kexec_buffer(void)
if (!ima_kexec_buffer_size)
return -ENOENT;
memblock_free_late(ima_kexec_buffer_phys,
memblock_phys_free(ima_kexec_buffer_phys,
ima_kexec_buffer_size);
ima_kexec_buffer_phys = 0;
+23 -50
View File
@@ -58,8 +58,8 @@ static u8 *emit_code(u8 *ptr, u32 bytes, unsigned int len)
#define EMIT_ENDBR() EMIT(gen_endbr(), 4)
#define EMIT_ENDBR_POISON() EMIT(gen_endbr_poison(), 4)
#else
#define EMIT_ENDBR()
#define EMIT_ENDBR_POISON()
#define EMIT_ENDBR() do { } while (0)
#define EMIT_ENDBR_POISON() do { } while (0)
#endif
static bool is_imm8(int value)
@@ -1649,8 +1649,8 @@ static int emit_spectre_bhb_barrier(u8 **pprog, u8 *ip,
return 0;
}
static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image, u8 *rw_image,
int oldproglen, struct jit_context *ctx, bool jmp_padding)
static int do_jit(struct bpf_verifier_env *env, struct bpf_prog *bpf_prog, int *addrs, u8 *image,
u8 *rw_image, int oldproglen, struct jit_context *ctx, bool jmp_padding)
{
bool tail_call_reachable = bpf_prog->aux->tail_call_reachable;
struct bpf_insn *insn = bpf_prog->insnsi;
@@ -1663,7 +1663,7 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image, u8 *rw_image
void __percpu *priv_stack_ptr;
int i, excnt = 0;
int ilen, proglen = 0;
u8 *prog = temp;
u8 *ip, *prog = temp;
u32 stack_depth;
int err;
@@ -1734,6 +1734,11 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image, u8 *rw_image
dst_reg = X86_REG_R9;
}
if (bpf_insn_is_indirect_target(env, bpf_prog, i - 1))
EMIT_ENDBR();
ip = image + addrs[i - 1] + (prog - temp);
switch (insn->code) {
/* ALU */
case BPF_ALU | BPF_ADD | BPF_X:
@@ -2440,8 +2445,6 @@ populate_extable:
/* call */
case BPF_JMP | BPF_CALL: {
u8 *ip = image + addrs[i - 1];
func = (u8 *) __bpf_call_base + imm32;
if (src_reg == BPF_PSEUDO_CALL && tail_call_reachable) {
LOAD_TAIL_CALL_CNT_PTR(stack_depth);
@@ -2465,7 +2468,8 @@ populate_extable:
if (imm32)
emit_bpf_tail_call_direct(bpf_prog,
&bpf_prog->aux->poke_tab[imm32 - 1],
&prog, image + addrs[i - 1],
&prog,
ip,
callee_regs_used,
stack_depth,
ctx);
@@ -2474,7 +2478,7 @@ populate_extable:
&prog,
callee_regs_used,
stack_depth,
image + addrs[i - 1],
ip,
ctx);
break;
@@ -2639,7 +2643,7 @@ emit_cond_jmp: /* Convert BPF opcode to x86 */
break;
case BPF_JMP | BPF_JA | BPF_X:
emit_indirect_jump(&prog, insn->dst_reg, image + addrs[i - 1]);
emit_indirect_jump(&prog, insn->dst_reg, ip);
break;
case BPF_JMP | BPF_JA:
case BPF_JMP32 | BPF_JA:
@@ -2729,8 +2733,6 @@ emit_jmp:
ctx->cleanup_addr = proglen;
if (bpf_prog_was_classic(bpf_prog) &&
!ns_capable_noaudit(&init_user_ns, CAP_SYS_ADMIN)) {
u8 *ip = image + addrs[i - 1];
if (emit_spectre_bhb_barrier(&prog, ip, bpf_prog))
return -EINVAL;
}
@@ -3713,17 +3715,15 @@ struct x64_jit_data {
#define MAX_PASSES 20
#define PADDING_PASSES (MAX_PASSES - 5)
struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog)
{
struct bpf_binary_header *rw_header = NULL;
struct bpf_binary_header *header = NULL;
struct bpf_prog *tmp, *orig_prog = prog;
void __percpu *priv_stack_ptr = NULL;
struct x64_jit_data *jit_data;
int priv_stack_alloc_sz;
int proglen, oldproglen = 0;
struct jit_context ctx = {};
bool tmp_blinded = false;
bool extra_pass = false;
bool padding = false;
u8 *rw_image = NULL;
@@ -3733,27 +3733,13 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
int i;
if (!prog->jit_requested)
return orig_prog;
tmp = bpf_jit_blind_constants(prog);
/*
* If blinding was requested and we failed during blinding,
* we must fall back to the interpreter.
*/
if (IS_ERR(tmp))
return orig_prog;
if (tmp != prog) {
tmp_blinded = true;
prog = tmp;
}
return prog;
jit_data = prog->aux->jit_data;
if (!jit_data) {
jit_data = kzalloc_obj(*jit_data);
if (!jit_data) {
prog = orig_prog;
goto out;
}
if (!jit_data)
return prog;
prog->aux->jit_data = jit_data;
}
priv_stack_ptr = prog->aux->priv_stack_ptr;
@@ -3765,10 +3751,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
priv_stack_alloc_sz = round_up(prog->aux->stack_depth, 8) +
2 * PRIV_STACK_GUARD_SZ;
priv_stack_ptr = __alloc_percpu_gfp(priv_stack_alloc_sz, 8, GFP_KERNEL);
if (!priv_stack_ptr) {
prog = orig_prog;
if (!priv_stack_ptr)
goto out_priv_stack;
}
priv_stack_init_guard(priv_stack_ptr, priv_stack_alloc_sz);
prog->aux->priv_stack_ptr = priv_stack_ptr;
@@ -3786,10 +3770,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
goto skip_init_addrs;
}
addrs = kvmalloc_objs(*addrs, prog->len + 1);
if (!addrs) {
prog = orig_prog;
if (!addrs)
goto out_addrs;
}
/*
* Before first pass, make a rough estimation of addrs[]
@@ -3811,7 +3793,7 @@ skip_init_addrs:
for (pass = 0; pass < MAX_PASSES || image; pass++) {
if (!padding && pass >= PADDING_PASSES)
padding = true;
proglen = do_jit(prog, addrs, image, rw_image, oldproglen, &ctx, padding);
proglen = do_jit(env, prog, addrs, image, rw_image, oldproglen, &ctx, padding);
if (proglen <= 0) {
out_image:
image = NULL;
@@ -3820,8 +3802,6 @@ out_image:
sizeof(rw_header->size));
bpf_jit_binary_pack_free(header, rw_header);
}
/* Fall back to interpreter mode */
prog = orig_prog;
if (extra_pass) {
prog->bpf_func = NULL;
prog->jited = 0;
@@ -3852,10 +3832,8 @@ out_image:
header = bpf_jit_binary_pack_alloc(roundup(proglen, align) + extable_size,
&image, align, &rw_header, &rw_image,
jit_fill_hole);
if (!header) {
prog = orig_prog;
if (!header)
goto out_addrs;
}
prog->aux->extable = (void *) image + roundup(proglen, align);
}
oldproglen = proglen;
@@ -3908,8 +3886,6 @@ out_image:
prog->bpf_func = (void *)image + cfi_get_offset();
prog->jited = 1;
prog->jited_len = proglen - cfi_get_offset();
} else {
prog = orig_prog;
}
if (!image || !prog->is_func || extra_pass) {
@@ -3925,10 +3901,7 @@ out_priv_stack:
kfree(jit_data);
prog->aux->jit_data = NULL;
}
out:
if (tmp_blinded)
bpf_jit_prog_release_other(prog, prog == orig_prog ?
tmp : orig_prog);
return prog;
}
+5 -30
View File
@@ -2518,38 +2518,22 @@ bool bpf_jit_needs_zext(void)
return true;
}
struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog)
{
struct bpf_binary_header *header = NULL;
struct bpf_prog *tmp, *orig_prog = prog;
int proglen, oldproglen = 0;
struct jit_context ctx = {};
bool tmp_blinded = false;
u8 *image = NULL;
int *addrs;
int pass;
int i;
if (!prog->jit_requested)
return orig_prog;
tmp = bpf_jit_blind_constants(prog);
/*
* If blinding was requested and we failed during blinding,
* we must fall back to the interpreter.
*/
if (IS_ERR(tmp))
return orig_prog;
if (tmp != prog) {
tmp_blinded = true;
prog = tmp;
}
return prog;
addrs = kmalloc_objs(*addrs, prog->len);
if (!addrs) {
prog = orig_prog;
goto out;
}
if (!addrs)
return prog;
/*
* Before first pass, make a rough estimation of addrs[]
@@ -2574,7 +2558,6 @@ out_image:
image = NULL;
if (header)
bpf_jit_binary_free(header);
prog = orig_prog;
goto out_addrs;
}
if (image) {
@@ -2588,10 +2571,8 @@ out_image:
if (proglen == oldproglen) {
header = bpf_jit_binary_alloc(proglen, &image,
1, jit_fill_hole);
if (!header) {
prog = orig_prog;
if (!header)
goto out_addrs;
}
}
oldproglen = proglen;
cond_resched();
@@ -2604,16 +2585,10 @@ out_image:
prog->bpf_func = (void *)image;
prog->jited = 1;
prog->jited_len = proglen;
} else {
prog = orig_prog;
}
out_addrs:
kfree(addrs);
out:
if (tmp_blinded)
bpf_jit_prog_release_other(prog, prog == orig_prog ?
tmp : orig_prog);
return prog;
}
+1 -4
View File
@@ -34,10 +34,7 @@ static
void __init __efi_memmap_free(u64 phys, unsigned long size, unsigned long flags)
{
if (flags & EFI_MEMMAP_MEMBLOCK) {
if (slab_is_available())
memblock_free_late(phys, size);
else
memblock_phys_free(phys, size);
memblock_phys_free(phys, size);
} else if (flags & EFI_MEMMAP_SLAB) {
struct page *p = pfn_to_page(PHYS_PFN(phys));
unsigned int order = get_order(size);
+1 -1
View File
@@ -372,7 +372,7 @@ void __init efi_reserve_boot_services(void)
* doesn't make sense as far as the firmware is
* concerned, but it does provide us with a way to tag
* those regions that must not be paired with
* memblock_free_late().
* memblock_phys_free().
*/
md->attribute |= EFI_MEMORY_RUNTIME;
}
+5 -2
View File
@@ -654,8 +654,11 @@ int __init acpi_numa_init(void)
}
last_real_pxm = fake_pxm;
fake_pxm++;
acpi_table_parse_cedt(ACPI_CEDT_TYPE_CFMWS, acpi_parse_cfmws,
&fake_pxm);
/* No need to expand numa nodes if CXL is disabled */
if (IS_ENABLED(CONFIG_CXL_ACPI))
acpi_table_parse_cedt(ACPI_CEDT_TYPE_CFMWS, acpi_parse_cfmws,
&fake_pxm);
if (cnt < 0)
return cnt;
+10
View File
@@ -187,6 +187,16 @@ config SSIF_IPMI_BMC
The driver implements the BMC side of the SMBus system
interface (SSIF).
config SSIF_IPMI_BMC_KUNIT_TEST
bool "KUnit tests for SSIF IPMI BMC driver" if !KUNIT_ALL_TESTS
depends on KUNIT=y
depends on SSIF_IPMI_BMC
default KUNIT_ALL_TESTS
help
This option builds unit tests that exercise the SSIF BMC state
machine, including request handling, response transmission,
and error paths such as aborted or truncated transfers.
config IPMB_DEVICE_INTERFACE
tristate 'IPMB Interface handler'
depends on I2C
+5 -5
View File
@@ -987,7 +987,7 @@ static int deliver_response(struct ipmi_smi *intf, struct ipmi_recv_msg *msg)
mutex_lock(&intf->user_msgs_mutex);
list_add_tail(&msg->link, &intf->user_msgs);
mutex_unlock(&intf->user_msgs_mutex);
queue_work(system_wq, &intf->smi_work);
queue_work(system_percpu_wq, &intf->smi_work);
}
return rv;
@@ -4977,7 +4977,7 @@ void ipmi_smi_msg_received(struct ipmi_smi *intf,
if (run_to_completion)
smi_work(&intf->smi_work);
else
queue_work(system_wq, &intf->smi_work);
queue_work(system_percpu_wq, &intf->smi_work);
}
EXPORT_SYMBOL(ipmi_smi_msg_received);
@@ -4987,7 +4987,7 @@ void ipmi_smi_watchdog_pretimeout(struct ipmi_smi *intf)
return;
atomic_set(&intf->watchdog_pretimeouts_to_deliver, 1);
queue_work(system_wq, &intf->smi_work);
queue_work(system_percpu_wq, &intf->smi_work);
}
EXPORT_SYMBOL(ipmi_smi_watchdog_pretimeout);
@@ -5162,7 +5162,7 @@ static bool ipmi_timeout_handler(struct ipmi_smi *intf,
flags);
}
queue_work(system_wq, &intf->smi_work);
queue_work(system_percpu_wq, &intf->smi_work);
return need_timer;
}
@@ -5218,7 +5218,7 @@ static void ipmi_timeout(struct timer_list *unused)
if (atomic_read(&stop_operation))
return;
queue_work(system_wq, &ipmi_timer_work);
queue_work(system_percpu_wq, &ipmi_timer_work);
}
static void need_waiter(struct ipmi_smi *intf)
+24 -17
View File
@@ -1268,8 +1268,10 @@ static void shutdown_ssif(void *send_info)
ssif_info->stopping = true;
timer_delete_sync(&ssif_info->watch_timer);
timer_delete_sync(&ssif_info->retry_timer);
if (ssif_info->thread)
if (ssif_info->thread) {
kthread_stop(ssif_info->thread);
ssif_info->thread = NULL;
}
}
static void ssif_remove(struct i2c_client *client)
@@ -1658,6 +1660,7 @@ static int ssif_probe(struct i2c_client *client)
int len = 0;
int i;
u8 slave_addr = 0;
unsigned int thread_num;
struct ssif_addr_info *addr_info = NULL;
mutex_lock(&ssif_infos_mutex);
@@ -1876,22 +1879,17 @@ static int ssif_probe(struct i2c_client *client)
ssif_info->handlers.request_events = request_events;
ssif_info->handlers.set_need_watch = ssif_set_need_watch;
{
unsigned int thread_num;
thread_num = ((i2c_adapter_id(ssif_info->client->adapter)
<< 8) |
ssif_info->client->addr);
init_completion(&ssif_info->wake_thread);
ssif_info->thread = kthread_run(ipmi_ssif_thread, ssif_info,
"kssif%4.4x", thread_num);
if (IS_ERR(ssif_info->thread)) {
rv = PTR_ERR(ssif_info->thread);
dev_notice(&ssif_info->client->dev,
"Could not start kernel thread: error %d\n",
rv);
goto out;
}
thread_num = ((i2c_adapter_id(ssif_info->client->adapter) << 8) |
ssif_info->client->addr);
init_completion(&ssif_info->wake_thread);
ssif_info->thread = kthread_run(ipmi_ssif_thread, ssif_info,
"kssif%4.4x", thread_num);
if (IS_ERR(ssif_info->thread)) {
rv = PTR_ERR(ssif_info->thread);
dev_notice(&ssif_info->client->dev,
"Could not start kernel thread: error %d\n",
rv);
goto out;
}
dev_set_drvdata(&ssif_info->client->dev, ssif_info);
@@ -1916,6 +1914,15 @@ static int ssif_probe(struct i2c_client *client)
out:
if (rv) {
/*
* If ipmi_register_smi() starts the interface, it will
* call shutdown and that will free the thread and set
* it to NULL. Otherwise it must be freed here.
*/
if (ssif_info->thread) {
kthread_stop(ssif_info->thread);
ssif_info->thread = NULL;
}
if (addr_info)
addr_info->client = NULL;
+396 -9
View File
@@ -18,6 +18,9 @@
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/ipmi_ssif_bmc.h>
#if IS_ENABLED(CONFIG_SSIF_IPMI_BMC_KUNIT_TEST)
#include <kunit/test.h>
#endif
#define DEVICE_NAME "ipmi-ssif-host"
@@ -163,6 +166,8 @@ static ssize_t ssif_bmc_read(struct file *file, char __user *buf, size_t count,
spin_unlock_irqrestore(&ssif_bmc->lock, flags);
ret = copy_to_user(buf, &msg, count);
if (ret > 0)
ret = -EFAULT;
}
return (ret < 0) ? ret : count;
@@ -456,6 +461,15 @@ static bool supported_write_cmd(u8 cmd)
return false;
}
static bool supported_write_start_cmd(u8 cmd)
{
if (cmd == SSIF_IPMI_SINGLEPART_WRITE ||
cmd == SSIF_IPMI_MULTIPART_WRITE_START)
return true;
return false;
}
/* Process the IPMI response that will be read by master */
static void handle_read_processed(struct ssif_bmc_ctx *ssif_bmc, u8 *val)
{
@@ -558,7 +572,7 @@ static void process_request_part(struct ssif_bmc_ctx *ssif_bmc)
len = ssif_bmc->request.len + part->length;
/* Do the bound check here, not allow the request len exceed 254 bytes */
if (len > IPMI_SSIF_PAYLOAD_MAX) {
dev_warn(&ssif_bmc->client->dev,
dev_dbg(&ssif_bmc->client->dev,
"Warn: Request exceeded 254 bytes, aborting");
/* Request too long, aborting */
ssif_bmc->aborting = true;
@@ -604,7 +618,7 @@ static void on_read_requested_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val)
ssif_bmc->state == SSIF_START ||
ssif_bmc->state == SSIF_REQ_RECVING ||
ssif_bmc->state == SSIF_RES_SENDING) {
dev_warn(&ssif_bmc->client->dev,
dev_dbg(&ssif_bmc->client->dev,
"Warn: %s unexpected READ REQUESTED in state=%s\n",
__func__, state_to_string(ssif_bmc->state));
ssif_bmc->state = SSIF_ABORTING;
@@ -613,7 +627,7 @@ static void on_read_requested_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val)
} else if (ssif_bmc->state == SSIF_SMBUS_CMD) {
if (!supported_read_cmd(ssif_bmc->part_buf.smbus_cmd)) {
dev_warn(&ssif_bmc->client->dev, "Warn: Unknown SMBus read command=0x%x",
dev_dbg(&ssif_bmc->client->dev, "Warn: Unknown SMBus read command=0x%x",
ssif_bmc->part_buf.smbus_cmd);
ssif_bmc->aborting = true;
}
@@ -648,7 +662,7 @@ static void on_read_processed_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val)
ssif_bmc->state == SSIF_START ||
ssif_bmc->state == SSIF_REQ_RECVING ||
ssif_bmc->state == SSIF_SMBUS_CMD) {
dev_warn(&ssif_bmc->client->dev,
dev_dbg(&ssif_bmc->client->dev,
"Warn: %s unexpected READ PROCESSED in state=%s\n",
__func__, state_to_string(ssif_bmc->state));
ssif_bmc->state = SSIF_ABORTING;
@@ -673,7 +687,7 @@ static void on_write_requested_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val)
} else if (ssif_bmc->state == SSIF_START ||
ssif_bmc->state == SSIF_REQ_RECVING ||
ssif_bmc->state == SSIF_RES_SENDING) {
dev_warn(&ssif_bmc->client->dev,
dev_dbg(&ssif_bmc->client->dev,
"Warn: %s unexpected WRITE REQUEST in state=%s\n",
__func__, state_to_string(ssif_bmc->state));
ssif_bmc->state = SSIF_ABORTING;
@@ -688,7 +702,7 @@ static void on_write_received_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val)
{
if (ssif_bmc->state == SSIF_READY ||
ssif_bmc->state == SSIF_RES_SENDING) {
dev_warn(&ssif_bmc->client->dev,
dev_dbg(&ssif_bmc->client->dev,
"Warn: %s unexpected WRITE RECEIVED in state=%s\n",
__func__, state_to_string(ssif_bmc->state));
ssif_bmc->state = SSIF_ABORTING;
@@ -698,7 +712,7 @@ static void on_write_received_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val)
} else if (ssif_bmc->state == SSIF_SMBUS_CMD) {
if (!supported_write_cmd(ssif_bmc->part_buf.smbus_cmd)) {
dev_warn(&ssif_bmc->client->dev, "Warn: Unknown SMBus write command=0x%x",
dev_dbg(&ssif_bmc->client->dev, "Warn: Unknown SMBus write command=0x%x",
ssif_bmc->part_buf.smbus_cmd);
ssif_bmc->aborting = true;
}
@@ -707,6 +721,11 @@ static void on_write_received_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val)
ssif_bmc->state = SSIF_ABORTING;
else
ssif_bmc->state = SSIF_REQ_RECVING;
} else if (ssif_bmc->state == SSIF_ABORTING) {
if (supported_write_start_cmd(*val)) {
ssif_bmc->state = SSIF_SMBUS_CMD;
ssif_bmc->aborting = false;
}
}
/* This is response sending state */
@@ -722,7 +741,7 @@ static void on_stop_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val)
ssif_bmc->state == SSIF_START ||
ssif_bmc->state == SSIF_SMBUS_CMD ||
ssif_bmc->state == SSIF_ABORTING) {
dev_warn(&ssif_bmc->client->dev,
dev_dbg(&ssif_bmc->client->dev,
"Warn: %s unexpected SLAVE STOP in state=%s\n",
__func__, state_to_string(ssif_bmc->state));
ssif_bmc->state = SSIF_READY;
@@ -789,7 +808,7 @@ static int ssif_bmc_cb(struct i2c_client *client, enum i2c_slave_event event, u8
break;
default:
dev_warn(&ssif_bmc->client->dev, "Warn: Unknown i2c slave event\n");
dev_dbg(&ssif_bmc->client->dev, "Warn: Unknown i2c slave event\n");
break;
}
@@ -843,6 +862,7 @@ static void ssif_bmc_remove(struct i2c_client *client)
{
struct ssif_bmc_ctx *ssif_bmc = i2c_get_clientdata(client);
timer_delete_sync(&ssif_bmc->response_timer);
i2c_slave_unregister(client);
misc_deregister(&ssif_bmc->miscdev);
}
@@ -869,6 +889,373 @@ static struct i2c_driver ssif_bmc_driver = {
.id_table = ssif_bmc_id,
};
#if IS_ENABLED(CONFIG_SSIF_IPMI_BMC_KUNIT_TEST)
struct ssif_bmc_test_ctx {
struct ssif_bmc_ctx ssif_bmc;
struct i2c_client client;
struct i2c_adapter adapter;
struct i2c_algorithm algo;
};
static int ssif_bmc_test_init(struct kunit *test)
{
struct ssif_bmc_test_ctx *test_ctx;
test_ctx = kunit_kzalloc(test, sizeof(*test_ctx), GFP_KERNEL);
if (!test_ctx)
return -ENOMEM;
test_ctx->adapter.algo = &test_ctx->algo;
test_ctx->client.addr = 0x20;
test_ctx->client.adapter = &test_ctx->adapter;
spin_lock_init(&test_ctx->ssif_bmc.lock);
init_waitqueue_head(&test_ctx->ssif_bmc.wait_queue);
test_ctx->ssif_bmc.client = &test_ctx->client;
i2c_set_clientdata(&test_ctx->client, &test_ctx->ssif_bmc);
test->priv = test_ctx;
return 0;
}
static void ssif_bmc_test_exit(struct kunit *test)
{
struct ssif_bmc_test_ctx *test_ctx = test->priv;
if (test_ctx->ssif_bmc.response_timer_inited)
timer_delete_sync(&test_ctx->ssif_bmc.response_timer);
}
static int ssif_bmc_test_run_event_val(struct ssif_bmc_test_ctx *test_ctx,
enum i2c_slave_event event,
u8 *value)
{
return ssif_bmc_cb(&test_ctx->client, event, value);
}
static int ssif_bmc_test_run_event(struct ssif_bmc_test_ctx *test_ctx,
enum i2c_slave_event event, u8 value)
{
return ssif_bmc_test_run_event_val(test_ctx, event, &value);
}
static void ssif_bmc_test_singlepart_req(struct kunit *test)
{
struct ssif_bmc_test_ctx *test_ctx = test->priv;
struct ssif_bmc_ctx *ssif_bmc = &test_ctx->ssif_bmc;
ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_REQUESTED,
GET_8BIT_ADDR(test_ctx->client.addr));
ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED,
SSIF_IPMI_SINGLEPART_WRITE);
ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 2);
ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 0xaa);
ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 0x55);
KUNIT_EXPECT_EQ(test,
ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_STOP, 0), -EBUSY);
KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_READY);
KUNIT_EXPECT_TRUE(test, ssif_bmc->request_available);
KUNIT_EXPECT_TRUE(test, ssif_bmc->busy);
KUNIT_EXPECT_FALSE(test, ssif_bmc->aborting);
KUNIT_EXPECT_EQ(test, ssif_bmc->request.len, 2);
KUNIT_EXPECT_EQ(test, ssif_bmc->request.payload[0], 0xaa);
KUNIT_EXPECT_EQ(test, ssif_bmc->request.payload[1], 0x55);
KUNIT_EXPECT_TRUE(test, ssif_bmc->response_timer_inited);
}
static void ssif_bmc_test_restart_write_without_stop(struct kunit *test)
{
struct ssif_bmc_test_ctx *test_ctx = test->priv;
struct ssif_bmc_ctx *ssif_bmc = &test_ctx->ssif_bmc;
KUNIT_ASSERT_EQ(test,
ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_REQUESTED,
GET_8BIT_ADDR(test_ctx->client.addr)), 0);
KUNIT_ASSERT_EQ(test,
ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED,
SSIF_IPMI_SINGLEPART_WRITE), 0);
KUNIT_ASSERT_EQ(test,
ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 2), 0);
KUNIT_ASSERT_EQ(test,
ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 0xde), 0);
KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_REQ_RECVING);
/* Write transaction, without stop, and new request coming */
ssif_bmc_test_singlepart_req(test);
}
static void ssif_bmc_test_restart_after_invalid_command(struct kunit *test)
{
struct ssif_bmc_test_ctx *test_ctx = test->priv;
struct ssif_bmc_ctx *ssif_bmc = &test_ctx->ssif_bmc;
KUNIT_ASSERT_EQ(test,
ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_REQUESTED,
GET_8BIT_ADDR(test_ctx->client.addr)), 0);
KUNIT_ASSERT_EQ(test,
ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 0xff), 0);
KUNIT_ASSERT_EQ(test,
ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 1), 0);
KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_ABORTING);
KUNIT_EXPECT_TRUE(test, ssif_bmc->aborting);
/* After An Invalid Command, expect could handle new request */
ssif_bmc_test_singlepart_req(test);
}
static void ssif_bmc_test_singlepart_read_response_completion(struct kunit *test)
{
struct ssif_bmc_test_ctx *test_ctx = test->priv;
struct ssif_bmc_ctx *ssif_bmc = &test_ctx->ssif_bmc;
u8 value;
ssif_bmc->state = SSIF_SMBUS_CMD;
ssif_bmc->part_buf.smbus_cmd = SSIF_IPMI_SINGLEPART_READ;
ssif_bmc->response.len = 2;
ssif_bmc->response.payload[0] = 0x11;
ssif_bmc->response.payload[1] = 0x22;
ssif_bmc->response_in_progress = true;
ssif_bmc->is_singlepart_read = true;
ssif_bmc->pec_support = true;
value = 0;
KUNIT_ASSERT_EQ(test,
ssif_bmc_test_run_event_val(test_ctx, I2C_SLAVE_READ_REQUESTED,
&value), 0);
KUNIT_EXPECT_EQ(test, value, 2);
KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_RES_SENDING);
value = 0;
KUNIT_EXPECT_EQ(test,
ssif_bmc_test_run_event_val(test_ctx, I2C_SLAVE_READ_PROCESSED,
&value), 0);
KUNIT_EXPECT_EQ(test, value, 0x11);
value = 0;
KUNIT_EXPECT_EQ(test,
ssif_bmc_test_run_event_val(test_ctx, I2C_SLAVE_READ_PROCESSED,
&value), 0);
KUNIT_EXPECT_EQ(test, value, 0x22);
value = 0;
KUNIT_EXPECT_EQ(test,
ssif_bmc_test_run_event_val(test_ctx, I2C_SLAVE_READ_PROCESSED,
&value), 0);
KUNIT_EXPECT_EQ(test, value, ssif_bmc->part_buf.pec);
KUNIT_EXPECT_EQ(test,
ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_STOP, 0), 0);
KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_READY);
KUNIT_EXPECT_FALSE(test, ssif_bmc->response_in_progress);
KUNIT_EXPECT_EQ(test, ssif_bmc->response.len, 0);
}
static void ssif_bmc_test_stop_during_start_discards_partial_request(struct kunit *test)
{
struct ssif_bmc_test_ctx *test_ctx = test->priv;
struct ssif_bmc_ctx *ssif_bmc = &test_ctx->ssif_bmc;
KUNIT_ASSERT_EQ(test,
ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_REQUESTED,
GET_8BIT_ADDR(test_ctx->client.addr)), 0);
KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_START);
KUNIT_EXPECT_EQ(test,
ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_STOP, 0), 0);
KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_READY);
KUNIT_EXPECT_FALSE(test, ssif_bmc->request_available);
KUNIT_EXPECT_EQ(test, ssif_bmc->msg_idx, 0);
KUNIT_EXPECT_EQ(test,
ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_REQUESTED,
GET_8BIT_ADDR(test_ctx->client.addr)), 0);
KUNIT_EXPECT_EQ(test,
ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED,
SSIF_IPMI_SINGLEPART_WRITE), 0);
KUNIT_EXPECT_EQ(test,
ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 1), 0);
KUNIT_EXPECT_EQ(test,
ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 0x77), 0);
KUNIT_EXPECT_EQ(test,
ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_STOP, 0), -EBUSY);
KUNIT_EXPECT_TRUE(test, ssif_bmc->request_available);
KUNIT_EXPECT_EQ(test, ssif_bmc->request.len, 1);
KUNIT_EXPECT_EQ(test, ssif_bmc->request.payload[0], 0x77);
}
static void ssif_bmc_test_read_interrupts_partial_write(struct kunit *test)
{
struct ssif_bmc_test_ctx *test_ctx = test->priv;
struct ssif_bmc_ctx *ssif_bmc = &test_ctx->ssif_bmc;
u8 value = 0xff;
KUNIT_ASSERT_EQ(test,
ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_REQUESTED,
GET_8BIT_ADDR(test_ctx->client.addr)), 0);
KUNIT_ASSERT_EQ(test,
ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED,
SSIF_IPMI_SINGLEPART_WRITE), 0);
KUNIT_ASSERT_EQ(test,
ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 2), 0);
KUNIT_ASSERT_EQ(test,
ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 0xab), 0);
KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_REQ_RECVING);
KUNIT_EXPECT_EQ(test,
ssif_bmc_test_run_event_val(test_ctx, I2C_SLAVE_READ_REQUESTED,
&value), 0);
KUNIT_EXPECT_EQ(test, value, 0);
KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_ABORTING);
KUNIT_EXPECT_EQ(test,
ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_STOP, 0), 0);
KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_READY);
KUNIT_EXPECT_FALSE(test, ssif_bmc->request_available);
KUNIT_EXPECT_EQ(test, ssif_bmc->request.len, 0);
KUNIT_EXPECT_EQ(test,
ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_REQUESTED,
GET_8BIT_ADDR(test_ctx->client.addr)), 0);
KUNIT_EXPECT_EQ(test,
ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED,
SSIF_IPMI_SINGLEPART_WRITE), 0);
KUNIT_EXPECT_EQ(test,
ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 1), 0);
KUNIT_EXPECT_EQ(test,
ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 0xcd), 0);
KUNIT_EXPECT_EQ(test,
ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_STOP, 0), -EBUSY);
KUNIT_EXPECT_TRUE(test, ssif_bmc->request_available);
KUNIT_EXPECT_EQ(test, ssif_bmc->request.len, 1);
KUNIT_EXPECT_EQ(test, ssif_bmc->request.payload[0], 0xcd);
}
static void ssif_bmc_test_write_interrupts_response_send(struct kunit *test)
{
struct ssif_bmc_test_ctx *test_ctx = test->priv;
struct ssif_bmc_ctx *ssif_bmc = &test_ctx->ssif_bmc;
u8 value = 0;
ssif_bmc->state = SSIF_SMBUS_CMD;
ssif_bmc->part_buf.smbus_cmd = SSIF_IPMI_SINGLEPART_READ;
ssif_bmc->response.len = 1;
ssif_bmc->response.payload[0] = 0x66;
ssif_bmc->response_in_progress = true;
ssif_bmc->is_singlepart_read = true;
KUNIT_ASSERT_EQ(test,
ssif_bmc_test_run_event_val(test_ctx, I2C_SLAVE_READ_REQUESTED,
&value), 0);
KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_RES_SENDING);
/* READ_REQUESTED transaction */
ssif_bmc_test_singlepart_req(test);
}
static void ssif_bmc_test_write_interrupts_response_sending(struct kunit *test)
{
struct ssif_bmc_test_ctx *test_ctx = test->priv;
struct ssif_bmc_ctx *ssif_bmc = &test_ctx->ssif_bmc;
u8 value = 0;
ssif_bmc->state = SSIF_SMBUS_CMD;
ssif_bmc->part_buf.smbus_cmd = SSIF_IPMI_SINGLEPART_READ;
ssif_bmc->response.len = 1;
ssif_bmc->response.payload[0] = 0x66;
ssif_bmc->response_in_progress = true;
ssif_bmc->is_singlepart_read = true;
KUNIT_ASSERT_EQ(test,
ssif_bmc_test_run_event_val(test_ctx, I2C_SLAVE_READ_REQUESTED,
&value), 0);
KUNIT_EXPECT_EQ(test, ssif_bmc->state, SSIF_RES_SENDING);
KUNIT_ASSERT_EQ(test,
ssif_bmc_test_run_event_val(test_ctx, I2C_SLAVE_READ_PROCESSED,
&value), 0);
KUNIT_EXPECT_EQ(test, value, 0x66);
/* READ_REQUESTED transaction */
ssif_bmc_test_singlepart_req(test);
}
static void ssif_bmc_test_timeout_interrupt_allows_retry(struct kunit *test)
{
struct ssif_bmc_test_ctx *test_ctx = test->priv;
struct ssif_bmc_ctx *ssif_bmc = &test_ctx->ssif_bmc;
KUNIT_ASSERT_EQ(test,
ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_REQUESTED,
GET_8BIT_ADDR(test_ctx->client.addr)), 0);
KUNIT_ASSERT_EQ(test,
ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED,
SSIF_IPMI_SINGLEPART_WRITE), 0);
KUNIT_ASSERT_EQ(test,
ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 1), 0);
KUNIT_ASSERT_EQ(test,
ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 0x21), 0);
KUNIT_ASSERT_EQ(test,
ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_STOP, 0), -EBUSY);
KUNIT_ASSERT_TRUE(test, timer_pending(&ssif_bmc->response_timer));
timer_delete_sync(&ssif_bmc->response_timer);
response_timeout(&ssif_bmc->response_timer);
KUNIT_EXPECT_FALSE(test, ssif_bmc->busy);
KUNIT_EXPECT_TRUE(test, ssif_bmc->aborting);
KUNIT_EXPECT_FALSE(test, ssif_bmc->response_timer_inited);
KUNIT_EXPECT_EQ(test,
ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_REQUESTED,
GET_8BIT_ADDR(test_ctx->client.addr)), 0);
KUNIT_EXPECT_EQ(test,
ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED,
SSIF_IPMI_SINGLEPART_WRITE), 0);
KUNIT_EXPECT_FALSE(test, ssif_bmc->aborting);
KUNIT_EXPECT_EQ(test,
ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 1), 0);
KUNIT_EXPECT_EQ(test,
ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_WRITE_RECEIVED, 0x22), 0);
KUNIT_EXPECT_EQ(test,
ssif_bmc_test_run_event(test_ctx, I2C_SLAVE_STOP, 0), -EBUSY);
KUNIT_EXPECT_TRUE(test, ssif_bmc->request_available);
KUNIT_EXPECT_EQ(test, ssif_bmc->request.len, 1);
KUNIT_EXPECT_EQ(test, ssif_bmc->request.payload[0], 0x22);
}
static struct kunit_case ssif_bmc_test_cases[] = {
KUNIT_CASE(ssif_bmc_test_singlepart_req),
KUNIT_CASE(ssif_bmc_test_restart_write_without_stop),
KUNIT_CASE(ssif_bmc_test_restart_after_invalid_command),
KUNIT_CASE(ssif_bmc_test_singlepart_read_response_completion),
KUNIT_CASE(ssif_bmc_test_stop_during_start_discards_partial_request),
KUNIT_CASE(ssif_bmc_test_read_interrupts_partial_write),
KUNIT_CASE(ssif_bmc_test_write_interrupts_response_send),
KUNIT_CASE(ssif_bmc_test_write_interrupts_response_sending),
KUNIT_CASE(ssif_bmc_test_timeout_interrupt_allows_retry),
{}
};
static struct kunit_suite ssif_bmc_test_suite = {
.name = "ssif_bmc_test",
.init = ssif_bmc_test_init,
.exit = ssif_bmc_test_exit,
.test_cases = ssif_bmc_test_cases,
};
kunit_test_suite(ssif_bmc_test_suite);
#endif
module_i2c_driver(ssif_bmc_driver);
MODULE_AUTHOR("Quan Nguyen <quan@os.amperecomputing.com>");
+1 -1
View File
@@ -15,7 +15,7 @@ cxl_core-y += hdm.o
cxl_core-y += pmu.o
cxl_core-y += cdat.o
cxl_core-$(CONFIG_TRACING) += trace.o
cxl_core-$(CONFIG_CXL_REGION) += region.o
cxl_core-$(CONFIG_CXL_REGION) += region.o region_pmem.o region_dax.o
cxl_core-$(CONFIG_CXL_MCE) += mce.o
cxl_core-$(CONFIG_CXL_FEATURES) += features.o
cxl_core-$(CONFIG_CXL_EDAC_MEM_FEATURES) += edac.o
+4
View File
@@ -50,6 +50,8 @@ int cxl_get_poison_by_endpoint(struct cxl_port *port);
struct cxl_region *cxl_dpa_to_region(const struct cxl_memdev *cxlmd, u64 dpa);
u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
u64 dpa);
int devm_cxl_add_dax_region(struct cxl_region *cxlr);
int devm_cxl_add_pmem_region(struct cxl_region *cxlr);
#else
static inline u64 cxl_dpa_to_hpa(struct cxl_region *cxlr,
@@ -224,4 +226,6 @@ int cxl_set_feature(struct cxl_mailbox *cxl_mbox, const uuid_t *feat_uuid,
u16 *return_code);
#endif
resource_size_t cxl_rcd_component_reg_phys(struct device *dev,
struct cxl_dport *dport);
#endif /* __CXL_CORE_H__ */
+1 -1
View File
@@ -170,7 +170,7 @@ static struct cxl_hdm *devm_cxl_setup_hdm(struct cxl_port *port,
}
parse_hdm_decoder_caps(cxlhdm);
if (cxlhdm->decoder_count == 0) {
if (cxlhdm->decoder_count < 0) {
dev_err(dev, "Spec violation. Caps invalid\n");
return ERR_PTR(-ENXIO);
}
+8 -9
View File
@@ -893,7 +893,7 @@ out:
}
EXPORT_SYMBOL_NS_GPL(cxl_enumerate_cmds, "CXL");
void cxl_event_trace_record(const struct cxl_memdev *cxlmd,
void cxl_event_trace_record(struct cxl_memdev *cxlmd,
enum cxl_event_log_type type,
enum cxl_event_type event_type,
const uuid_t *uuid, union cxl_event *evt)
@@ -920,6 +920,7 @@ void cxl_event_trace_record(const struct cxl_memdev *cxlmd,
* translations. Take topology mutation locks and lookup
* { HPA, REGION } from { DPA, MEMDEV } in the event record.
*/
guard(device)(&cxlmd->dev);
guard(rwsem_read)(&cxl_rwsem.region);
guard(rwsem_read)(&cxl_rwsem.dpa);
@@ -968,7 +969,7 @@ void cxl_event_trace_record(const struct cxl_memdev *cxlmd,
}
EXPORT_SYMBOL_NS_GPL(cxl_event_trace_record, "CXL");
static void __cxl_event_trace_record(const struct cxl_memdev *cxlmd,
static void __cxl_event_trace_record(struct cxl_memdev *cxlmd,
enum cxl_event_log_type type,
struct cxl_event_record_raw *record)
{
@@ -1521,23 +1522,21 @@ int cxl_mailbox_init(struct cxl_mailbox *cxl_mbox, struct device *host)
}
EXPORT_SYMBOL_NS_GPL(cxl_mailbox_init, "CXL");
struct cxl_memdev_state *cxl_memdev_state_create(struct device *dev)
struct cxl_memdev_state *cxl_memdev_state_create(struct device *dev, u64 serial,
u16 dvsec)
{
struct cxl_memdev_state *mds;
int rc;
mds = devm_kzalloc(dev, sizeof(*mds), GFP_KERNEL);
mds = devm_cxl_dev_state_create(dev, CXL_DEVTYPE_CLASSMEM, serial,
dvsec, struct cxl_memdev_state, cxlds,
true);
if (!mds) {
dev_err(dev, "No memory available\n");
return ERR_PTR(-ENOMEM);
}
mutex_init(&mds->event.log_lock);
mds->cxlds.dev = dev;
mds->cxlds.reg_map.host = dev;
mds->cxlds.cxl_mbox.host = dev;
mds->cxlds.reg_map.resource = CXL_RESOURCE_NONE;
mds->cxlds.type = CXL_DEVTYPE_CLASSMEM;
rc = devm_cxl_register_mce_notifier(dev, &mds->mce_notifier);
if (rc == -EOPNOTSUPP)
+27
View File
@@ -204,6 +204,9 @@ bool cxl_memdev_has_poison_cmd(struct cxl_memdev *cxlmd,
{
struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds);
if (!mds)
return 0;
return test_bit(cmd, mds->poison.enabled_cmds);
}
@@ -656,6 +659,30 @@ static void detach_memdev(struct work_struct *work)
static struct lock_class_key cxl_memdev_key;
struct cxl_dev_state *_devm_cxl_dev_state_create(struct device *dev,
enum cxl_devtype type,
u64 serial, u16 dvsec,
size_t size, bool has_mbox)
{
struct cxl_dev_state *cxlds = devm_kzalloc(dev, size, GFP_KERNEL);
if (!cxlds)
return NULL;
cxlds->dev = dev;
cxlds->type = type;
cxlds->serial = serial;
cxlds->cxl_dvsec = dvsec;
cxlds->reg_map.host = dev;
cxlds->reg_map.resource = CXL_RESOURCE_NONE;
if (has_mbox)
cxlds->cxl_mbox.host = dev;
return cxlds;
}
EXPORT_SYMBOL_NS_GPL(_devm_cxl_dev_state_create, "CXL");
static struct cxl_memdev *cxl_memdev_alloc(struct cxl_dev_state *cxlds,
const struct file_operations *fops,
const struct cxl_memdev_attach *attach)
+57
View File
@@ -696,6 +696,63 @@ bool cxl_endpoint_decoder_reset_detected(struct cxl_port *port)
}
EXPORT_SYMBOL_NS_GPL(cxl_endpoint_decoder_reset_detected, "CXL");
static int cxl_rcrb_get_comp_regs(struct pci_dev *pdev,
struct cxl_register_map *map,
struct cxl_dport *dport)
{
resource_size_t component_reg_phys;
*map = (struct cxl_register_map) {
.host = &pdev->dev,
.resource = CXL_RESOURCE_NONE,
};
component_reg_phys = cxl_rcd_component_reg_phys(&pdev->dev, dport);
if (component_reg_phys == CXL_RESOURCE_NONE)
return -ENXIO;
map->resource = component_reg_phys;
map->reg_type = CXL_REGLOC_RBI_COMPONENT;
map->max_size = CXL_COMPONENT_REG_BLOCK_SIZE;
return 0;
}
int cxl_pci_setup_regs(struct pci_dev *pdev, enum cxl_regloc_type type,
struct cxl_register_map *map)
{
int rc;
rc = cxl_find_regblock(pdev, type, map);
/*
* If the Register Locator DVSEC does not exist, check if it
* is an RCH and try to extract the Component Registers from
* an RCRB.
*/
if (rc && type == CXL_REGLOC_RBI_COMPONENT && is_cxl_restricted(pdev)) {
struct cxl_dport *dport;
struct cxl_port *port __free(put_cxl_port) =
cxl_pci_find_port(pdev, &dport);
if (!port)
return -EPROBE_DEFER;
rc = cxl_rcrb_get_comp_regs(pdev, map, dport);
if (rc)
return rc;
rc = cxl_dport_map_rcd_linkcap(pdev, dport);
if (rc)
return rc;
} else if (rc) {
return rc;
}
return cxl_setup_regs(map);
}
EXPORT_SYMBOL_NS_GPL(cxl_pci_setup_regs, "CXL");
int cxl_pci_get_bandwidth(struct pci_dev *pdev, struct access_coordinate *c)
{
int speed, bw;
+161 -318
View File
@@ -485,22 +485,14 @@ static ssize_t interleave_ways_show(struct device *dev,
static const struct attribute_group *get_cxl_region_target_group(void);
static ssize_t interleave_ways_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
static int set_interleave_ways(struct cxl_region *cxlr, int val)
{
struct cxl_region *cxlr = to_cxl_region(dev);
struct cxl_root_decoder *cxlrd = cxlr->cxlrd;
struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld;
struct cxl_region_params *p = &cxlr->params;
unsigned int val, save;
int rc;
int save, rc;
u8 iw;
rc = kstrtouint(buf, 0, &val);
if (rc)
return rc;
rc = ways_to_eiw(val, &iw);
if (rc)
return rc;
@@ -515,9 +507,7 @@ static ssize_t interleave_ways_store(struct device *dev,
return -EINVAL;
}
ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region);
if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem)))
return rc;
lockdep_assert_held_write(&cxl_rwsem.region);
if (p->state >= CXL_CONFIG_INTERLEAVE_ACTIVE)
return -EBUSY;
@@ -525,10 +515,31 @@ static ssize_t interleave_ways_store(struct device *dev,
save = p->interleave_ways;
p->interleave_ways = val;
rc = sysfs_update_group(&cxlr->dev.kobj, get_cxl_region_target_group());
if (rc) {
if (rc)
p->interleave_ways = save;
return rc;
}
static ssize_t interleave_ways_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
{
struct cxl_region *cxlr = to_cxl_region(dev);
int val;
int rc;
rc = kstrtoint(buf, 0, &val);
if (rc)
return rc;
ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region);
if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem)))
return rc;
rc = set_interleave_ways(cxlr, val);
if (rc)
return rc;
}
return len;
}
@@ -548,21 +559,14 @@ static ssize_t interleave_granularity_show(struct device *dev,
return sysfs_emit(buf, "%d\n", p->interleave_granularity);
}
static ssize_t interleave_granularity_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
static int set_interleave_granularity(struct cxl_region *cxlr, int val)
{
struct cxl_region *cxlr = to_cxl_region(dev);
struct cxl_root_decoder *cxlrd = cxlr->cxlrd;
struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld;
struct cxl_region_params *p = &cxlr->params;
int rc, val;
int rc;
u16 ig;
rc = kstrtoint(buf, 0, &val);
if (rc)
return rc;
rc = granularity_to_eig(val, &ig);
if (rc)
return rc;
@@ -578,14 +582,33 @@ static ssize_t interleave_granularity_store(struct device *dev,
if (cxld->interleave_ways > 1 && val != cxld->interleave_granularity)
return -EINVAL;
ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region);
if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem)))
return rc;
lockdep_assert_held_write(&cxl_rwsem.region);
if (p->state >= CXL_CONFIG_INTERLEAVE_ACTIVE)
return -EBUSY;
p->interleave_granularity = val;
return 0;
}
static ssize_t interleave_granularity_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
{
struct cxl_region *cxlr = to_cxl_region(dev);
int rc, val;
rc = kstrtoint(buf, 0, &val);
if (rc)
return rc;
ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region);
if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem)))
return rc;
rc = set_interleave_granularity(cxlr, val);
if (rc)
return rc;
return len;
}
@@ -767,6 +790,22 @@ static ssize_t extended_linear_cache_size_show(struct device *dev,
}
static DEVICE_ATTR_RO(extended_linear_cache_size);
static ssize_t locked_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct cxl_region *cxlr = to_cxl_region(dev);
int rc;
ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region);
if ((rc = ACQUIRE_ERR(rwsem_read_intr, &rwsem)))
return rc;
rc = test_bit(CXL_REGION_F_LOCK, &cxlr->flags);
return sysfs_emit(buf, "%d\n", rc);
}
static DEVICE_ATTR_RO(locked);
static struct attribute *cxl_region_attrs[] = {
&dev_attr_uuid.attr,
&dev_attr_commit.attr,
@@ -776,6 +815,7 @@ static struct attribute *cxl_region_attrs[] = {
&dev_attr_size.attr,
&dev_attr_mode.attr,
&dev_attr_extended_linear_cache_size.attr,
&dev_attr_locked.attr,
NULL,
};
@@ -1063,6 +1103,14 @@ static int cxl_rr_ep_add(struct cxl_region_ref *cxl_rr,
if (!cxld->region) {
cxld->region = cxlr;
/*
* Now that cxld->region is set the intermediate staging state
* can be cleared.
*/
if (cxld == &cxled->cxld &&
cxled->state == CXL_DECODER_STATE_AUTO_STAGED)
cxled->state = CXL_DECODER_STATE_AUTO;
get_device(&cxlr->dev);
}
@@ -1804,6 +1852,7 @@ static int cxl_region_attach_auto(struct cxl_region *cxlr,
pos = p->nr_targets;
p->targets[pos] = cxled;
cxled->pos = pos;
cxled->state = CXL_DECODER_STATE_AUTO_STAGED;
p->nr_targets++;
return 0;
@@ -2153,6 +2202,47 @@ static int cxl_region_attach(struct cxl_region *cxlr,
return 0;
}
static int cxl_region_by_target(struct device *dev, const void *data)
{
const struct cxl_endpoint_decoder *cxled = data;
struct cxl_region_params *p;
struct cxl_region *cxlr;
if (!is_cxl_region(dev))
return 0;
cxlr = to_cxl_region(dev);
p = &cxlr->params;
return p->targets[cxled->pos] == cxled;
}
/*
* When an auto-region fails to assemble the decoder may be listed as a target,
* but not fully attached.
*/
static void cxl_cancel_auto_attach(struct cxl_endpoint_decoder *cxled)
{
struct cxl_region_params *p;
struct cxl_region *cxlr;
int pos = cxled->pos;
if (cxled->state != CXL_DECODER_STATE_AUTO_STAGED)
return;
struct device *dev __free(put_device) =
bus_find_device(&cxl_bus_type, NULL, cxled, cxl_region_by_target);
if (!dev)
return;
cxlr = to_cxl_region(dev);
p = &cxlr->params;
p->nr_targets--;
cxled->state = CXL_DECODER_STATE_AUTO;
cxled->pos = -1;
p->targets[pos] = NULL;
}
static struct cxl_region *
__cxl_decoder_detach(struct cxl_region *cxlr,
struct cxl_endpoint_decoder *cxled, int pos,
@@ -2176,8 +2266,10 @@ __cxl_decoder_detach(struct cxl_region *cxlr,
cxled = p->targets[pos];
} else {
cxlr = cxled->cxld.region;
if (!cxlr)
if (!cxlr) {
cxl_cancel_auto_attach(cxled);
return NULL;
}
p = &cxlr->params;
}
@@ -2650,7 +2742,8 @@ static ssize_t create_ram_region_show(struct device *dev,
}
static struct cxl_region *__create_region(struct cxl_root_decoder *cxlrd,
enum cxl_partition_mode mode, int id)
enum cxl_partition_mode mode, int id,
enum cxl_decoder_type target_type)
{
int rc;
@@ -2672,7 +2765,7 @@ static struct cxl_region *__create_region(struct cxl_root_decoder *cxlrd,
return ERR_PTR(-EBUSY);
}
return devm_cxl_add_region(cxlrd, id, mode, CXL_DECODER_HOSTONLYMEM);
return devm_cxl_add_region(cxlrd, id, mode, target_type);
}
static ssize_t create_region_store(struct device *dev, const char *buf,
@@ -2686,7 +2779,7 @@ static ssize_t create_region_store(struct device *dev, const char *buf,
if (rc != 1)
return -EINVAL;
cxlr = __create_region(cxlrd, mode, id);
cxlr = __create_region(cxlrd, mode, id, CXL_DECODER_HOSTONLYMEM);
if (IS_ERR(cxlr))
return PTR_ERR(cxlr);
@@ -2757,46 +2850,6 @@ static ssize_t delete_region_store(struct device *dev,
}
DEVICE_ATTR_WO(delete_region);
static void cxl_pmem_region_release(struct device *dev)
{
struct cxl_pmem_region *cxlr_pmem = to_cxl_pmem_region(dev);
int i;
for (i = 0; i < cxlr_pmem->nr_mappings; i++) {
struct cxl_memdev *cxlmd = cxlr_pmem->mapping[i].cxlmd;
put_device(&cxlmd->dev);
}
kfree(cxlr_pmem);
}
static const struct attribute_group *cxl_pmem_region_attribute_groups[] = {
&cxl_base_attribute_group,
NULL,
};
const struct device_type cxl_pmem_region_type = {
.name = "cxl_pmem_region",
.release = cxl_pmem_region_release,
.groups = cxl_pmem_region_attribute_groups,
};
bool is_cxl_pmem_region(struct device *dev)
{
return dev->type == &cxl_pmem_region_type;
}
EXPORT_SYMBOL_NS_GPL(is_cxl_pmem_region, "CXL");
struct cxl_pmem_region *to_cxl_pmem_region(struct device *dev)
{
if (dev_WARN_ONCE(dev, !is_cxl_pmem_region(dev),
"not a cxl_pmem_region device\n"))
return NULL;
return container_of(dev, struct cxl_pmem_region, dev);
}
EXPORT_SYMBOL_NS_GPL(to_cxl_pmem_region, "CXL");
struct cxl_poison_context {
struct cxl_port *port;
int part;
@@ -2950,13 +3003,15 @@ static int __cxl_dpa_to_region(struct device *dev, void *arg)
struct cxl_region *cxl_dpa_to_region(const struct cxl_memdev *cxlmd, u64 dpa)
{
struct cxl_dpa_to_region_context ctx;
struct cxl_port *port;
struct cxl_port *port = cxlmd->endpoint;
if (!cxlmd->dev.driver)
return NULL;
ctx = (struct cxl_dpa_to_region_context) {
.dpa = dpa,
};
port = cxlmd->endpoint;
if (port && is_cxl_endpoint(port) && cxl_num_decoders_committed(port))
if (cxl_num_decoders_committed(port))
device_for_each_child(&port->dev, &ctx, __cxl_dpa_to_region);
return ctx.cxlr;
@@ -3450,249 +3505,6 @@ static int region_offset_to_dpa_result(struct cxl_region *cxlr, u64 offset,
return -ENXIO;
}
static struct lock_class_key cxl_pmem_region_key;
static int cxl_pmem_region_alloc(struct cxl_region *cxlr)
{
struct cxl_region_params *p = &cxlr->params;
struct cxl_nvdimm_bridge *cxl_nvb;
struct device *dev;
int i;
guard(rwsem_read)(&cxl_rwsem.region);
if (p->state != CXL_CONFIG_COMMIT)
return -ENXIO;
struct cxl_pmem_region *cxlr_pmem __free(kfree) =
kzalloc_flex(*cxlr_pmem, mapping, p->nr_targets);
if (!cxlr_pmem)
return -ENOMEM;
cxlr_pmem->hpa_range.start = p->res->start;
cxlr_pmem->hpa_range.end = p->res->end;
/* Snapshot the region configuration underneath the cxl_rwsem.region */
cxlr_pmem->nr_mappings = p->nr_targets;
for (i = 0; i < p->nr_targets; i++) {
struct cxl_endpoint_decoder *cxled = p->targets[i];
struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
struct cxl_pmem_region_mapping *m = &cxlr_pmem->mapping[i];
/*
* Regions never span CXL root devices, so by definition the
* bridge for one device is the same for all.
*/
if (i == 0) {
cxl_nvb = cxl_find_nvdimm_bridge(cxlmd->endpoint);
if (!cxl_nvb)
return -ENODEV;
cxlr->cxl_nvb = cxl_nvb;
}
m->cxlmd = cxlmd;
get_device(&cxlmd->dev);
m->start = cxled->dpa_res->start;
m->size = resource_size(cxled->dpa_res);
m->position = i;
}
dev = &cxlr_pmem->dev;
device_initialize(dev);
lockdep_set_class(&dev->mutex, &cxl_pmem_region_key);
device_set_pm_not_required(dev);
dev->parent = &cxlr->dev;
dev->bus = &cxl_bus_type;
dev->type = &cxl_pmem_region_type;
cxlr_pmem->cxlr = cxlr;
cxlr->cxlr_pmem = no_free_ptr(cxlr_pmem);
return 0;
}
static void cxl_dax_region_release(struct device *dev)
{
struct cxl_dax_region *cxlr_dax = to_cxl_dax_region(dev);
kfree(cxlr_dax);
}
static const struct attribute_group *cxl_dax_region_attribute_groups[] = {
&cxl_base_attribute_group,
NULL,
};
const struct device_type cxl_dax_region_type = {
.name = "cxl_dax_region",
.release = cxl_dax_region_release,
.groups = cxl_dax_region_attribute_groups,
};
static bool is_cxl_dax_region(struct device *dev)
{
return dev->type == &cxl_dax_region_type;
}
struct cxl_dax_region *to_cxl_dax_region(struct device *dev)
{
if (dev_WARN_ONCE(dev, !is_cxl_dax_region(dev),
"not a cxl_dax_region device\n"))
return NULL;
return container_of(dev, struct cxl_dax_region, dev);
}
EXPORT_SYMBOL_NS_GPL(to_cxl_dax_region, "CXL");
static struct lock_class_key cxl_dax_region_key;
static struct cxl_dax_region *cxl_dax_region_alloc(struct cxl_region *cxlr)
{
struct cxl_region_params *p = &cxlr->params;
struct cxl_dax_region *cxlr_dax;
struct device *dev;
guard(rwsem_read)(&cxl_rwsem.region);
if (p->state != CXL_CONFIG_COMMIT)
return ERR_PTR(-ENXIO);
cxlr_dax = kzalloc_obj(*cxlr_dax);
if (!cxlr_dax)
return ERR_PTR(-ENOMEM);
cxlr_dax->hpa_range.start = p->res->start;
cxlr_dax->hpa_range.end = p->res->end;
dev = &cxlr_dax->dev;
cxlr_dax->cxlr = cxlr;
device_initialize(dev);
lockdep_set_class(&dev->mutex, &cxl_dax_region_key);
device_set_pm_not_required(dev);
dev->parent = &cxlr->dev;
dev->bus = &cxl_bus_type;
dev->type = &cxl_dax_region_type;
return cxlr_dax;
}
static void cxlr_pmem_unregister(void *_cxlr_pmem)
{
struct cxl_pmem_region *cxlr_pmem = _cxlr_pmem;
struct cxl_region *cxlr = cxlr_pmem->cxlr;
struct cxl_nvdimm_bridge *cxl_nvb = cxlr->cxl_nvb;
/*
* Either the bridge is in ->remove() context under the device_lock(),
* or cxlr_release_nvdimm() is cancelling the bridge's release action
* for @cxlr_pmem and doing it itself (while manually holding the bridge
* lock).
*/
device_lock_assert(&cxl_nvb->dev);
cxlr->cxlr_pmem = NULL;
cxlr_pmem->cxlr = NULL;
device_unregister(&cxlr_pmem->dev);
}
static void cxlr_release_nvdimm(void *_cxlr)
{
struct cxl_region *cxlr = _cxlr;
struct cxl_nvdimm_bridge *cxl_nvb = cxlr->cxl_nvb;
scoped_guard(device, &cxl_nvb->dev) {
if (cxlr->cxlr_pmem)
devm_release_action(&cxl_nvb->dev, cxlr_pmem_unregister,
cxlr->cxlr_pmem);
}
cxlr->cxl_nvb = NULL;
put_device(&cxl_nvb->dev);
}
/**
* devm_cxl_add_pmem_region() - add a cxl_region-to-nd_region bridge
* @cxlr: parent CXL region for this pmem region bridge device
*
* Return: 0 on success negative error code on failure.
*/
static int devm_cxl_add_pmem_region(struct cxl_region *cxlr)
{
struct cxl_pmem_region *cxlr_pmem;
struct cxl_nvdimm_bridge *cxl_nvb;
struct device *dev;
int rc;
rc = cxl_pmem_region_alloc(cxlr);
if (rc)
return rc;
cxlr_pmem = cxlr->cxlr_pmem;
cxl_nvb = cxlr->cxl_nvb;
dev = &cxlr_pmem->dev;
rc = dev_set_name(dev, "pmem_region%d", cxlr->id);
if (rc)
goto err;
rc = device_add(dev);
if (rc)
goto err;
dev_dbg(&cxlr->dev, "%s: register %s\n", dev_name(dev->parent),
dev_name(dev));
scoped_guard(device, &cxl_nvb->dev) {
if (cxl_nvb->dev.driver)
rc = devm_add_action_or_reset(&cxl_nvb->dev,
cxlr_pmem_unregister,
cxlr_pmem);
else
rc = -ENXIO;
}
if (rc)
goto err_bridge;
/* @cxlr carries a reference on @cxl_nvb until cxlr_release_nvdimm */
return devm_add_action_or_reset(&cxlr->dev, cxlr_release_nvdimm, cxlr);
err:
put_device(dev);
err_bridge:
put_device(&cxl_nvb->dev);
cxlr->cxl_nvb = NULL;
return rc;
}
static void cxlr_dax_unregister(void *_cxlr_dax)
{
struct cxl_dax_region *cxlr_dax = _cxlr_dax;
device_unregister(&cxlr_dax->dev);
}
static int devm_cxl_add_dax_region(struct cxl_region *cxlr)
{
struct cxl_dax_region *cxlr_dax;
struct device *dev;
int rc;
cxlr_dax = cxl_dax_region_alloc(cxlr);
if (IS_ERR(cxlr_dax))
return PTR_ERR(cxlr_dax);
dev = &cxlr_dax->dev;
rc = dev_set_name(dev, "dax_region%d", cxlr->id);
if (rc)
goto err;
rc = device_add(dev);
if (rc)
goto err;
dev_dbg(&cxlr->dev, "%s: register %s\n", dev_name(dev->parent),
dev_name(dev));
return devm_add_action_or_reset(&cxlr->dev, cxlr_dax_unregister,
cxlr_dax);
err:
put_device(dev);
return rc;
}
static int match_root_decoder(struct device *dev, const void *data)
{
const struct range *r1, *r2 = data;
@@ -3904,7 +3716,8 @@ static struct cxl_region *construct_region(struct cxl_root_decoder *cxlrd,
do {
cxlr = __create_region(cxlrd, cxlds->part[part].mode,
atomic_read(&cxlrd->region_id));
atomic_read(&cxlrd->region_id),
cxled->cxld.target_type);
} while (IS_ERR(cxlr) && PTR_ERR(cxlr) == -EBUSY);
if (IS_ERR(cxlr)) {
@@ -4175,6 +3988,36 @@ static int cxl_region_setup_poison(struct cxl_region *cxlr)
return devm_add_action_or_reset(dev, remove_debugfs, dentry);
}
static int region_contains_resource(struct device *dev, const void *data)
{
const struct resource *res = data;
struct cxl_region *cxlr;
struct cxl_region_params *p;
if (!is_cxl_region(dev))
return 0;
cxlr = to_cxl_region(dev);
p = &cxlr->params;
if (p->state != CXL_CONFIG_COMMIT)
return 0;
if (!p->res)
return 0;
return resource_contains(p->res, res) ? 1 : 0;
}
bool cxl_region_contains_resource(const struct resource *res)
{
guard(rwsem_read)(&cxl_rwsem.region);
struct device *dev __free(put_device) = bus_find_device(
&cxl_bus_type, NULL, res, region_contains_resource);
return !!dev;
}
EXPORT_SYMBOL_FOR_MODULES(cxl_region_contains_resource, "dax_hmem");
static int cxl_region_can_probe(struct cxl_region *cxlr)
{
struct cxl_region_params *p = &cxlr->params;
+106
View File
@@ -0,0 +1,106 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright(c) 2022 Intel Corporation. All rights reserved.
* Copyright(c) 2026 Meta Technologies Inc. All rights reserved.
*/
#include <linux/device.h>
#include <linux/slab.h>
#include <cxlmem.h>
#include <cxl.h>
#include "core.h"
static void cxl_dax_region_release(struct device *dev)
{
struct cxl_dax_region *cxlr_dax = to_cxl_dax_region(dev);
kfree(cxlr_dax);
}
static const struct attribute_group *cxl_dax_region_attribute_groups[] = {
&cxl_base_attribute_group,
NULL
};
const struct device_type cxl_dax_region_type = {
.name = "cxl_dax_region",
.release = cxl_dax_region_release,
.groups = cxl_dax_region_attribute_groups,
};
static bool is_cxl_dax_region(struct device *dev)
{
return dev->type == &cxl_dax_region_type;
}
struct cxl_dax_region *to_cxl_dax_region(struct device *dev)
{
if (dev_WARN_ONCE(dev, !is_cxl_dax_region(dev),
"not a cxl_dax_region device\n"))
return NULL;
return container_of(dev, struct cxl_dax_region, dev);
}
EXPORT_SYMBOL_NS_GPL(to_cxl_dax_region, "CXL");
static struct lock_class_key cxl_dax_region_key;
static struct cxl_dax_region *cxl_dax_region_alloc(struct cxl_region *cxlr)
{
struct cxl_region_params *p = &cxlr->params;
struct cxl_dax_region *cxlr_dax;
struct device *dev;
guard(rwsem_read)(&cxl_rwsem.region);
if (p->state != CXL_CONFIG_COMMIT)
return ERR_PTR(-ENXIO);
cxlr_dax = kzalloc_obj(*cxlr_dax);
if (!cxlr_dax)
return ERR_PTR(-ENOMEM);
cxlr_dax->hpa_range.start = p->res->start;
cxlr_dax->hpa_range.end = p->res->end;
dev = &cxlr_dax->dev;
cxlr_dax->cxlr = cxlr;
device_initialize(dev);
lockdep_set_class(&dev->mutex, &cxl_dax_region_key);
device_set_pm_not_required(dev);
dev->parent = &cxlr->dev;
dev->bus = &cxl_bus_type;
dev->type = &cxl_dax_region_type;
return cxlr_dax;
}
static void cxlr_dax_unregister(void *_cxlr_dax)
{
struct cxl_dax_region *cxlr_dax = _cxlr_dax;
device_unregister(&cxlr_dax->dev);
}
int devm_cxl_add_dax_region(struct cxl_region *cxlr)
{
struct device *dev;
int rc;
struct cxl_dax_region *cxlr_dax __free(put_cxl_dax_region) =
cxl_dax_region_alloc(cxlr);
if (IS_ERR(cxlr_dax))
return PTR_ERR(cxlr_dax);
dev = &cxlr_dax->dev;
rc = dev_set_name(dev, "dax_region%d", cxlr->id);
if (rc)
return rc;
rc = device_add(dev);
if (rc)
return rc;
dev_dbg(&cxlr->dev, "%s: register %s\n", dev_name(dev->parent),
dev_name(dev));
return devm_add_action_or_reset(&cxlr->dev, cxlr_dax_unregister,
no_free_ptr(cxlr_dax));
}
+191
View File
@@ -0,0 +1,191 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright(c) 2022 Intel Corporation. All rights reserved. */
#include <linux/device.h>
#include <linux/slab.h>
#include <cxlmem.h>
#include <cxl.h>
#include "core.h"
static void cxl_pmem_region_release(struct device *dev)
{
struct cxl_pmem_region *cxlr_pmem = to_cxl_pmem_region(dev);
int i;
for (i = 0; i < cxlr_pmem->nr_mappings; i++) {
struct cxl_memdev *cxlmd = cxlr_pmem->mapping[i].cxlmd;
put_device(&cxlmd->dev);
}
kfree(cxlr_pmem);
}
static const struct attribute_group *cxl_pmem_region_attribute_groups[] = {
&cxl_base_attribute_group,
NULL
};
const struct device_type cxl_pmem_region_type = {
.name = "cxl_pmem_region",
.release = cxl_pmem_region_release,
.groups = cxl_pmem_region_attribute_groups,
};
bool is_cxl_pmem_region(struct device *dev)
{
return dev->type == &cxl_pmem_region_type;
}
EXPORT_SYMBOL_NS_GPL(is_cxl_pmem_region, "CXL");
struct cxl_pmem_region *to_cxl_pmem_region(struct device *dev)
{
if (dev_WARN_ONCE(dev, !is_cxl_pmem_region(dev),
"not a cxl_pmem_region device\n"))
return NULL;
return container_of(dev, struct cxl_pmem_region, dev);
}
EXPORT_SYMBOL_NS_GPL(to_cxl_pmem_region, "CXL");
static struct lock_class_key cxl_pmem_region_key;
static int cxl_pmem_region_alloc(struct cxl_region *cxlr)
{
struct cxl_region_params *p = &cxlr->params;
struct cxl_nvdimm_bridge *cxl_nvb;
struct device *dev;
int i;
guard(rwsem_read)(&cxl_rwsem.region);
if (p->state != CXL_CONFIG_COMMIT)
return -ENXIO;
struct cxl_pmem_region *cxlr_pmem __free(kfree) =
kzalloc_flex(*cxlr_pmem, mapping, p->nr_targets);
if (!cxlr_pmem)
return -ENOMEM;
cxlr_pmem->hpa_range.start = p->res->start;
cxlr_pmem->hpa_range.end = p->res->end;
/* Snapshot the region configuration underneath the cxl_rwsem.region */
cxlr_pmem->nr_mappings = p->nr_targets;
for (i = 0; i < p->nr_targets; i++) {
struct cxl_endpoint_decoder *cxled = p->targets[i];
struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
struct cxl_pmem_region_mapping *m = &cxlr_pmem->mapping[i];
/*
* Regions never span CXL root devices, so by definition the
* bridge for one device is the same for all.
*/
if (i == 0) {
cxl_nvb = cxl_find_nvdimm_bridge(cxlmd->endpoint);
if (!cxl_nvb)
return -ENODEV;
cxlr->cxl_nvb = cxl_nvb;
}
m->cxlmd = cxlmd;
get_device(&cxlmd->dev);
m->start = cxled->dpa_res->start;
m->size = resource_size(cxled->dpa_res);
m->position = i;
}
dev = &cxlr_pmem->dev;
device_initialize(dev);
lockdep_set_class(&dev->mutex, &cxl_pmem_region_key);
device_set_pm_not_required(dev);
dev->parent = &cxlr->dev;
dev->bus = &cxl_bus_type;
dev->type = &cxl_pmem_region_type;
cxlr_pmem->cxlr = cxlr;
cxlr->cxlr_pmem = no_free_ptr(cxlr_pmem);
return 0;
}
static void cxlr_pmem_unregister(void *_cxlr_pmem)
{
struct cxl_pmem_region *cxlr_pmem = _cxlr_pmem;
struct cxl_region *cxlr = cxlr_pmem->cxlr;
struct cxl_nvdimm_bridge *cxl_nvb = cxlr->cxl_nvb;
/*
* Either the bridge is in ->remove() context under the device_lock(),
* or cxlr_release_nvdimm() is cancelling the bridge's release action
* for @cxlr_pmem and doing it itself (while manually holding the bridge
* lock).
*/
device_lock_assert(&cxl_nvb->dev);
cxlr->cxlr_pmem = NULL;
cxlr_pmem->cxlr = NULL;
device_unregister(&cxlr_pmem->dev);
}
static void cxlr_release_nvdimm(void *_cxlr)
{
struct cxl_region *cxlr = _cxlr;
struct cxl_nvdimm_bridge *cxl_nvb = cxlr->cxl_nvb;
scoped_guard(device, &cxl_nvb->dev) {
if (cxlr->cxlr_pmem)
devm_release_action(&cxl_nvb->dev, cxlr_pmem_unregister,
cxlr->cxlr_pmem);
}
cxlr->cxl_nvb = NULL;
put_device(&cxl_nvb->dev);
}
/**
* devm_cxl_add_pmem_region() - add a cxl_region-to-nd_region bridge
* @cxlr: parent CXL region for this pmem region bridge device
*
* Return: 0 on success negative error code on failure.
*/
int devm_cxl_add_pmem_region(struct cxl_region *cxlr)
{
struct cxl_pmem_region *cxlr_pmem;
struct cxl_nvdimm_bridge *cxl_nvb;
struct device *dev;
int rc;
rc = cxl_pmem_region_alloc(cxlr);
if (rc)
return rc;
cxlr_pmem = cxlr->cxlr_pmem;
cxl_nvb = cxlr->cxl_nvb;
dev = &cxlr_pmem->dev;
rc = dev_set_name(dev, "pmem_region%d", cxlr->id);
if (rc)
goto err;
rc = device_add(dev);
if (rc)
goto err;
dev_dbg(&cxlr->dev, "%s: register %s\n", dev_name(dev->parent),
dev_name(dev));
scoped_guard(device, &cxl_nvb->dev) {
if (cxl_nvb->dev.driver)
rc = devm_add_action_or_reset(&cxl_nvb->dev,
cxlr_pmem_unregister,
cxlr_pmem);
else
rc = -ENXIO;
}
if (rc)
goto err_bridge;
/* @cxlr carries a reference on @cxl_nvb until cxlr_release_nvdimm */
return devm_add_action_or_reset(&cxlr->dev, cxlr_release_nvdimm, cxlr);
err:
put_device(dev);
err_bridge:
put_device(&cxl_nvb->dev);
cxlr->cxl_nvb = NULL;
return rc;
}
-1
View File
@@ -641,4 +641,3 @@ resource_size_t cxl_rcd_component_reg_phys(struct device *dev,
return CXL_RESOURCE_NONE;
return __rcrb_to_component(dev, &dport->rcrb, CXL_RCRB_UPSTREAM);
}
EXPORT_SYMBOL_NS_GPL(cxl_rcd_component_reg_phys, "CXL");
+22 -101
View File
@@ -12,6 +12,7 @@
#include <linux/node.h>
#include <linux/io.h>
#include <linux/range.h>
#include <cxl/cxl.h>
extern const struct nvdimm_security_ops *cxl_security_ops;
@@ -77,7 +78,16 @@ static inline int cxl_hdm_decoder_count(u32 cap_hdr)
{
int val = FIELD_GET(CXL_HDM_DECODER_COUNT_MASK, cap_hdr);
return val ? val * 2 : 1;
switch (val) {
case 0:
return 1;
case 1 ... 8:
return val * 2;
case 9 ... 12:
return (val - 4) * 4;
default:
return -ENXIO;
}
}
/* Encode defined in CXL 2.0 8.2.5.12.7 HDM Decoder Control Register */
@@ -201,97 +211,6 @@ static inline int ways_to_eiw(unsigned int ways, u8 *eiw)
#define CXLDEV_MBOX_BG_CMD_COMMAND_VENDOR_MASK GENMASK_ULL(63, 48)
#define CXLDEV_MBOX_PAYLOAD_OFFSET 0x20
/*
* Using struct_group() allows for per register-block-type helper routines,
* without requiring block-type agnostic code to include the prefix.
*/
struct cxl_regs {
/*
* Common set of CXL Component register block base pointers
* @hdm_decoder: CXL 2.0 8.2.5.12 CXL HDM Decoder Capability Structure
* @ras: CXL 2.0 8.2.5.9 CXL RAS Capability Structure
*/
struct_group_tagged(cxl_component_regs, component,
void __iomem *hdm_decoder;
void __iomem *ras;
);
/*
* Common set of CXL Device register block base pointers
* @status: CXL 2.0 8.2.8.3 Device Status Registers
* @mbox: CXL 2.0 8.2.8.4 Mailbox Registers
* @memdev: CXL 2.0 8.2.8.5 Memory Device Registers
*/
struct_group_tagged(cxl_device_regs, device_regs,
void __iomem *status, *mbox, *memdev;
);
struct_group_tagged(cxl_pmu_regs, pmu_regs,
void __iomem *pmu;
);
/*
* RCH downstream port specific RAS register
* @aer: CXL 3.0 8.2.1.1 RCH Downstream Port RCRB
*/
struct_group_tagged(cxl_rch_regs, rch_regs,
void __iomem *dport_aer;
);
/*
* RCD upstream port specific PCIe cap register
* @pcie_cap: CXL 3.0 8.2.1.2 RCD Upstream Port RCRB
*/
struct_group_tagged(cxl_rcd_regs, rcd_regs,
void __iomem *rcd_pcie_cap;
);
};
struct cxl_reg_map {
bool valid;
int id;
unsigned long offset;
unsigned long size;
};
struct cxl_component_reg_map {
struct cxl_reg_map hdm_decoder;
struct cxl_reg_map ras;
};
struct cxl_device_reg_map {
struct cxl_reg_map status;
struct cxl_reg_map mbox;
struct cxl_reg_map memdev;
};
struct cxl_pmu_reg_map {
struct cxl_reg_map pmu;
};
/**
* struct cxl_register_map - DVSEC harvested register block mapping parameters
* @host: device for devm operations and logging
* @base: virtual base of the register-block-BAR + @block_offset
* @resource: physical resource base of the register block
* @max_size: maximum mapping size to perform register search
* @reg_type: see enum cxl_regloc_type
* @component_map: cxl_reg_map for component registers
* @device_map: cxl_reg_maps for device registers
* @pmu_map: cxl_reg_maps for CXL Performance Monitoring Units
*/
struct cxl_register_map {
struct device *host;
void __iomem *base;
resource_size_t resource;
resource_size_t max_size;
u8 reg_type;
union {
struct cxl_component_reg_map component_map;
struct cxl_device_reg_map device_map;
struct cxl_pmu_reg_map pmu_map;
};
};
void cxl_probe_component_regs(struct device *dev, void __iomem *base,
struct cxl_component_reg_map *map);
void cxl_probe_device_regs(struct device *dev, void __iomem *base,
@@ -312,8 +231,6 @@ int cxl_find_regblock(struct pci_dev *pdev, enum cxl_regloc_type type,
struct cxl_register_map *map);
int cxl_setup_regs(struct cxl_register_map *map);
struct cxl_dport;
resource_size_t cxl_rcd_component_reg_phys(struct device *dev,
struct cxl_dport *dport);
int cxl_dport_map_rcd_linkcap(struct pci_dev *pdev, struct cxl_dport *dport);
#define CXL_RESOURCE_NONE ((resource_size_t) -1)
@@ -333,6 +250,7 @@ int cxl_dport_map_rcd_linkcap(struct pci_dev *pdev, struct cxl_dport *dport);
#define CXL_DECODER_F_LOCK BIT(4)
#define CXL_DECODER_F_ENABLE BIT(5)
#define CXL_DECODER_F_NORMALIZED_ADDRESSING BIT(6)
#define CXL_DECODER_F_RESET_MASK (CXL_DECODER_F_ENABLE | CXL_DECODER_F_LOCK)
enum cxl_decoder_type {
CXL_DECODER_DEVMEM = 2,
@@ -378,12 +296,14 @@ struct cxl_decoder {
};
/*
* Track whether this decoder is reserved for region autodiscovery, or
* free for userspace provisioning.
* Track whether this decoder is free for userspace provisioning, reserved for
* region autodiscovery, whether it is started connecting (awaiting other
* peers), or has completed auto assembly.
*/
enum cxl_decoder_state {
CXL_DECODER_STATE_MANUAL,
CXL_DECODER_STATE_AUTO,
CXL_DECODER_STATE_AUTO_STAGED,
};
/**
@@ -497,11 +417,6 @@ struct cxl_region_params {
resource_size_t cache_size;
};
enum cxl_partition_mode {
CXL_PARTMODE_RAM,
CXL_PARTMODE_PMEM,
};
/*
* Indicate whether this region has been assembled by autodetection or
* userspace assembly. Prevent endpoint decoders outside of automatic
@@ -808,6 +723,7 @@ DEFINE_FREE(put_cxl_root, struct cxl_root *, if (_T) put_device(&_T->port.dev))
DEFINE_FREE(put_cxl_port, struct cxl_port *, if (!IS_ERR_OR_NULL(_T)) put_device(&_T->dev))
DEFINE_FREE(put_cxl_root_decoder, struct cxl_root_decoder *, if (!IS_ERR_OR_NULL(_T)) put_device(&_T->cxlsd.cxld.dev))
DEFINE_FREE(put_cxl_region, struct cxl_region *, if (!IS_ERR_OR_NULL(_T)) put_device(&_T->dev))
DEFINE_FREE(put_cxl_dax_region, struct cxl_dax_region *, if (!IS_ERR_OR_NULL(_T)) put_device(&_T->dev))
int devm_cxl_enumerate_ports(struct cxl_memdev *cxlmd);
void cxl_bus_rescan(void);
@@ -939,6 +855,7 @@ struct cxl_pmem_region *to_cxl_pmem_region(struct device *dev);
int cxl_add_to_region(struct cxl_endpoint_decoder *cxled);
struct cxl_dax_region *to_cxl_dax_region(struct device *dev);
u64 cxl_port_get_spa_cache_alias(struct cxl_port *endpoint, u64 spa);
bool cxl_region_contains_resource(const struct resource *res);
#else
static inline bool is_cxl_pmem_region(struct device *dev)
{
@@ -961,6 +878,10 @@ static inline u64 cxl_port_get_spa_cache_alias(struct cxl_port *endpoint,
{
return 0;
}
static inline bool cxl_region_contains_resource(const struct resource *res)
{
return false;
}
#endif
void cxl_endpoint_parse_cdat(struct cxl_port *port);
+4 -86
View File
@@ -113,8 +113,6 @@ int devm_cxl_dpa_reserve(struct cxl_endpoint_decoder *cxled,
resource_size_t base, resource_size_t len,
resource_size_t skipped);
#define CXL_NR_PARTITIONS_MAX 2
struct cxl_dpa_info {
u64 size;
struct cxl_dpa_part_info {
@@ -373,87 +371,6 @@ struct cxl_security_state {
struct kernfs_node *sanitize_node;
};
/*
* enum cxl_devtype - delineate type-2 from a generic type-3 device
* @CXL_DEVTYPE_DEVMEM - Vendor specific CXL Type-2 device implementing HDM-D or
* HDM-DB, no requirement that this device implements a
* mailbox, or other memory-device-standard manageability
* flows.
* @CXL_DEVTYPE_CLASSMEM - Common class definition of a CXL Type-3 device with
* HDM-H and class-mandatory memory device registers
*/
enum cxl_devtype {
CXL_DEVTYPE_DEVMEM,
CXL_DEVTYPE_CLASSMEM,
};
/**
* struct cxl_dpa_perf - DPA performance property entry
* @dpa_range: range for DPA address
* @coord: QoS performance data (i.e. latency, bandwidth)
* @cdat_coord: raw QoS performance data from CDAT
* @qos_class: QoS Class cookies
*/
struct cxl_dpa_perf {
struct range dpa_range;
struct access_coordinate coord[ACCESS_COORDINATE_MAX];
struct access_coordinate cdat_coord[ACCESS_COORDINATE_MAX];
int qos_class;
};
/**
* struct cxl_dpa_partition - DPA partition descriptor
* @res: shortcut to the partition in the DPA resource tree (cxlds->dpa_res)
* @perf: performance attributes of the partition from CDAT
* @mode: operation mode for the DPA capacity, e.g. ram, pmem, dynamic...
*/
struct cxl_dpa_partition {
struct resource res;
struct cxl_dpa_perf perf;
enum cxl_partition_mode mode;
};
/**
* struct cxl_dev_state - The driver device state
*
* cxl_dev_state represents the CXL driver/device state. It provides an
* interface to mailbox commands as well as some cached data about the device.
* Currently only memory devices are represented.
*
* @dev: The device associated with this CXL state
* @cxlmd: The device representing the CXL.mem capabilities of @dev
* @reg_map: component and ras register mapping parameters
* @regs: Class device "Device" registers
* @cxl_dvsec: Offset to the PCIe device DVSEC
* @rcd: operating in RCD mode (CXL 3.0 9.11.8 CXL Devices Attached to an RCH)
* @media_ready: Indicate whether the device media is usable
* @dpa_res: Overall DPA resource tree for the device
* @part: DPA partition array
* @nr_partitions: Number of DPA partitions
* @serial: PCIe Device Serial Number
* @type: Generic Memory Class device or Vendor Specific Memory device
* @cxl_mbox: CXL mailbox context
* @cxlfs: CXL features context
*/
struct cxl_dev_state {
struct device *dev;
struct cxl_memdev *cxlmd;
struct cxl_register_map reg_map;
struct cxl_device_regs regs;
int cxl_dvsec;
bool rcd;
bool media_ready;
struct resource dpa_res;
struct cxl_dpa_partition part[CXL_NR_PARTITIONS_MAX];
unsigned int nr_partitions;
u64 serial;
enum cxl_devtype type;
struct cxl_mailbox cxl_mbox;
#ifdef CONFIG_CXL_FEATURES
struct cxl_features_state *cxlfs;
#endif
};
static inline resource_size_t cxl_pmem_size(struct cxl_dev_state *cxlds)
{
/*
@@ -858,13 +775,14 @@ int cxl_dev_state_identify(struct cxl_memdev_state *mds);
int cxl_await_media_ready(struct cxl_dev_state *cxlds);
int cxl_enumerate_cmds(struct cxl_memdev_state *mds);
int cxl_mem_dpa_fetch(struct cxl_memdev_state *mds, struct cxl_dpa_info *info);
struct cxl_memdev_state *cxl_memdev_state_create(struct device *dev);
struct cxl_memdev_state *cxl_memdev_state_create(struct device *dev, u64 serial,
u16 dvsec);
void set_exclusive_cxl_commands(struct cxl_memdev_state *mds,
unsigned long *cmds);
void clear_exclusive_cxl_commands(struct cxl_memdev_state *mds,
unsigned long *cmds);
void cxl_mem_get_event_records(struct cxl_memdev_state *mds, u32 status);
void cxl_event_trace_record(const struct cxl_memdev *cxlmd,
void cxl_event_trace_record(struct cxl_memdev *cxlmd,
enum cxl_event_log_type type,
enum cxl_event_type event_type,
const uuid_t *uuid, union cxl_event *evt);
@@ -923,7 +841,7 @@ int cxl_mem_sanitize(struct cxl_memdev *cxlmd, u16 cmd);
*/
struct cxl_hdm {
struct cxl_component_regs regs;
unsigned int decoder_count;
int decoder_count;
unsigned int target_count;
unsigned int interleave_mask;
unsigned long iw_cap_mask;
+13
View File
@@ -74,6 +74,17 @@ static inline bool cxl_pci_flit_256(struct pci_dev *pdev)
return lnksta2 & PCI_EXP_LNKSTA2_FLIT;
}
/*
* Assume that the caller has already validated that @pdev has CXL
* capabilities, any RCiEP with CXL capabilities is treated as a
* Restricted CXL Device (RCD) and finds upstream port and endpoint
* registers in a Root Complex Register Block (RCRB).
*/
static inline bool is_cxl_restricted(struct pci_dev *pdev)
{
return pci_pcie_type(pdev) == PCI_EXP_TYPE_RC_END;
}
struct cxl_dev_state;
void read_cdat_data(struct cxl_port *port);
@@ -101,4 +112,6 @@ static inline void devm_cxl_port_ras_setup(struct cxl_port *port)
}
#endif
int cxl_pci_setup_regs(struct pci_dev *pdev, enum cxl_regloc_type type,
struct cxl_register_map *map);
#endif /* __CXL_PCI_H__ */
+26 -77
View File
@@ -465,76 +465,6 @@ static int cxl_pci_setup_mailbox(struct cxl_memdev_state *mds, bool irq_avail)
return 0;
}
/*
* Assume that any RCIEP that emits the CXL memory expander class code
* is an RCD
*/
static bool is_cxl_restricted(struct pci_dev *pdev)
{
return pci_pcie_type(pdev) == PCI_EXP_TYPE_RC_END;
}
static int cxl_rcrb_get_comp_regs(struct pci_dev *pdev,
struct cxl_register_map *map,
struct cxl_dport *dport)
{
resource_size_t component_reg_phys;
*map = (struct cxl_register_map) {
.host = &pdev->dev,
.resource = CXL_RESOURCE_NONE,
};
struct cxl_port *port __free(put_cxl_port) =
cxl_pci_find_port(pdev, &dport);
if (!port)
return -EPROBE_DEFER;
component_reg_phys = cxl_rcd_component_reg_phys(&pdev->dev, dport);
if (component_reg_phys == CXL_RESOURCE_NONE)
return -ENXIO;
map->resource = component_reg_phys;
map->reg_type = CXL_REGLOC_RBI_COMPONENT;
map->max_size = CXL_COMPONENT_REG_BLOCK_SIZE;
return 0;
}
static int cxl_pci_setup_regs(struct pci_dev *pdev, enum cxl_regloc_type type,
struct cxl_register_map *map)
{
int rc;
rc = cxl_find_regblock(pdev, type, map);
/*
* If the Register Locator DVSEC does not exist, check if it
* is an RCH and try to extract the Component Registers from
* an RCRB.
*/
if (rc && type == CXL_REGLOC_RBI_COMPONENT && is_cxl_restricted(pdev)) {
struct cxl_dport *dport;
struct cxl_port *port __free(put_cxl_port) =
cxl_pci_find_port(pdev, &dport);
if (!port)
return -EPROBE_DEFER;
rc = cxl_rcrb_get_comp_regs(pdev, map, dport);
if (rc)
return rc;
rc = cxl_dport_map_rcd_linkcap(pdev, dport);
if (rc)
return rc;
} else if (rc) {
return rc;
}
return cxl_setup_regs(map);
}
static void free_event_buf(void *buf)
{
kvfree(buf);
@@ -865,25 +795,25 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
int rc, pmu_count;
unsigned int i;
bool irq_avail;
u16 dvsec;
rc = pcim_enable_device(pdev);
if (rc)
return rc;
pci_set_master(pdev);
mds = cxl_memdev_state_create(&pdev->dev);
dvsec = pci_find_dvsec_capability(pdev, PCI_VENDOR_ID_CXL,
PCI_DVSEC_CXL_DEVICE);
if (!dvsec)
pci_warn(pdev, "Device DVSEC not present, skip CXL.mem init\n");
mds = cxl_memdev_state_create(&pdev->dev, pci_get_dsn(pdev), dvsec);
if (IS_ERR(mds))
return PTR_ERR(mds);
cxlds = &mds->cxlds;
pci_set_drvdata(pdev, cxlds);
cxlds->rcd = is_cxl_restricted(pdev);
cxlds->serial = pci_get_dsn(pdev);
cxlds->cxl_dvsec = pci_find_dvsec_capability(
pdev, PCI_VENDOR_ID_CXL, PCI_DVSEC_CXL_DEVICE);
if (!cxlds->cxl_dvsec)
dev_warn(&pdev->dev,
"Device DVSEC not present, skip CXL.mem init\n");
rc = cxl_pci_setup_regs(pdev, CXL_REGLOC_RBI_MEMDEV, &map);
if (rc)
@@ -1030,6 +960,19 @@ static void cxl_error_resume(struct pci_dev *pdev)
dev->driver ? "successful" : "failed");
}
static int cxl_endpoint_decoder_clear_reset_flags(struct device *dev, void *data)
{
struct cxl_endpoint_decoder *cxled;
if (!is_endpoint_decoder(dev))
return 0;
cxled = to_cxl_endpoint_decoder(dev);
cxled->cxld.flags &= ~CXL_DECODER_F_RESET_MASK;
return 0;
}
static void cxl_reset_done(struct pci_dev *pdev)
{
struct cxl_dev_state *cxlds = pci_get_drvdata(pdev);
@@ -1043,8 +986,14 @@ static void cxl_reset_done(struct pci_dev *pdev)
* that no longer exists.
*/
guard(device)(&cxlmd->dev);
if (!cxlmd->dev.driver)
return;
if (cxlmd->endpoint &&
cxl_endpoint_decoder_reset_detected(cxlmd->endpoint)) {
device_for_each_child(&cxlmd->endpoint->dev, NULL,
cxl_endpoint_decoder_clear_reset_flags);
dev_crit(dev, "SBR happened without memory regions removal.\n");
dev_crit(dev, "System may be unstable if regions hosted system memory.\n");
add_taint(TAINT_USER, LOCKDEP_STILL_OK);
+4
View File
@@ -32,6 +32,9 @@ config DEV_DAX_HMEM
depends on EFI_SOFT_RESERVE
select NUMA_KEEP_MEMINFO if NUMA_MEMBLKS
default DEV_DAX
depends on CXL_ACPI || !CXL_ACPI
depends on CXL_PCI || !CXL_PCI
depends on CXL_BUS || !CXL_BUS
help
EFI 2.8 platforms, and others, may advertise 'specific purpose'
memory. For example, a high bandwidth memory pool. The
@@ -48,6 +51,7 @@ config DEV_DAX_CXL
tristate "CXL DAX: direct access to CXL RAM regions"
depends on CXL_BUS && CXL_REGION && DEV_DAX
default CXL_REGION && DEV_DAX
depends on DEV_DAX_HMEM || !DEV_DAX_HMEM
help
CXL RAM regions are either mapped by platform-firmware
and published in the initial system-memory map as "System RAM", mapped
+1 -2
View File
@@ -1,4 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
obj-y += hmem/
obj-$(CONFIG_DAX) += dax.o
obj-$(CONFIG_DEV_DAX) += device_dax.o
obj-$(CONFIG_DEV_DAX_KMEM) += kmem.o
@@ -10,5 +11,3 @@ dax-y += bus.o
device_dax-y := device.o
dax_pmem-y := pmem.o
dax_cxl-y := cxl.o
obj-y += hmem/
+17 -3
View File
@@ -10,6 +10,7 @@
#include "dax-private.h"
#include "bus.h"
static struct resource dax_regions = DEFINE_RES_MEM_NAMED(0, -1, "DAX Regions");
static DEFINE_MUTEX(dax_bus_lock);
/*
@@ -627,6 +628,7 @@ static void dax_region_unregister(void *region)
sysfs_remove_groups(&dax_region->dev->kobj,
dax_region_attribute_groups);
release_resource(&dax_region->res);
dax_region_put(dax_region);
}
@@ -635,6 +637,7 @@ struct dax_region *alloc_dax_region(struct device *parent, int region_id,
unsigned long flags)
{
struct dax_region *dax_region;
int rc;
/*
* The DAX core assumes that it can store its private data in
@@ -667,14 +670,25 @@ struct dax_region *alloc_dax_region(struct device *parent, int region_id,
.flags = IORESOURCE_MEM | flags,
};
if (sysfs_create_groups(&parent->kobj, dax_region_attribute_groups)) {
kfree(dax_region);
return NULL;
rc = request_resource(&dax_regions, &dax_region->res);
if (rc) {
dev_dbg(parent, "dax_region resource conflict for %pR\n",
&dax_region->res);
goto err_res;
}
if (sysfs_create_groups(&parent->kobj, dax_region_attribute_groups))
goto err_sysfs;
if (devm_add_action_or_reset(parent, dax_region_unregister, dax_region))
return NULL;
return dax_region;
err_sysfs:
release_resource(&dax_region->res);
err_res:
dax_region_put(dax_region);
return NULL;
}
EXPORT_SYMBOL_GPL(alloc_dax_region);
+20
View File
@@ -3,7 +3,9 @@
#ifndef __DAX_BUS_H__
#define __DAX_BUS_H__
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/range.h>
#include <linux/workqueue.h>
struct dev_dax;
struct resource;
@@ -49,6 +51,24 @@ void dax_driver_unregister(struct dax_device_driver *dax_drv);
void kill_dev_dax(struct dev_dax *dev_dax);
bool static_dev_dax(struct dev_dax *dev_dax);
struct hmem_platform_device {
struct platform_device pdev;
struct work_struct work;
bool did_probe;
};
static inline struct hmem_platform_device *
to_hmem_platform_device(struct platform_device *pdev)
{
return container_of(pdev, struct hmem_platform_device, pdev);
}
#if IS_ENABLED(CONFIG_DEV_DAX_HMEM)
void dax_hmem_flush_work(void);
#else
static inline void dax_hmem_flush_work(void) { }
#endif
#define MODULE_ALIAS_DAX_DEVICE(type) \
MODULE_ALIAS("dax:t" __stringify(type) "*")
#define DAX_DEVICE_MODALIAS_FMT "dax:t%d"
+27 -1
View File
@@ -38,10 +38,36 @@ static struct cxl_driver cxl_dax_region_driver = {
.id = CXL_DEVICE_DAX_REGION,
.drv = {
.suppress_bind_attrs = true,
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
};
module_cxl_driver(cxl_dax_region_driver);
static void cxl_dax_region_driver_register(struct work_struct *work)
{
dax_hmem_flush_work();
cxl_driver_register(&cxl_dax_region_driver);
}
static DECLARE_WORK(cxl_dax_region_driver_work, cxl_dax_region_driver_register);
static int __init cxl_dax_region_init(void)
{
/*
* Need to resolve a race with dax_hmem wanting to drive regions
* instead of CXL
*/
queue_work(system_long_wq, &cxl_dax_region_driver_work);
return 0;
}
module_init(cxl_dax_region_init);
static void __exit cxl_dax_region_exit(void)
{
flush_work(&cxl_dax_region_driver_work);
cxl_driver_unregister(&cxl_dax_region_driver);
}
module_exit(cxl_dax_region_exit);
MODULE_ALIAS_CXL(CXL_DEVICE_DAX_REGION);
MODULE_DESCRIPTION("CXL DAX: direct access to CXL regions");
MODULE_LICENSE("GPL");
+17 -8
View File
@@ -4,6 +4,7 @@
#include <linux/module.h>
#include <linux/dax.h>
#include <linux/mm.h>
#include "../bus.h"
static bool nohmem;
module_param_named(disable, nohmem, bool, 0444);
@@ -33,9 +34,21 @@ int walk_hmem_resources(struct device *host, walk_hmem_fn fn)
}
EXPORT_SYMBOL_GPL(walk_hmem_resources);
static void hmem_work(struct work_struct *work)
{
/* place holder until dax_hmem driver attaches */
}
static struct hmem_platform_device hmem_platform = {
.pdev = {
.name = "hmem_platform",
.id = 0,
},
.work = __WORK_INITIALIZER(hmem_platform.work, hmem_work),
};
static void __hmem_register_resource(int target_nid, struct resource *res)
{
struct platform_device *pdev;
struct resource *new;
int rc;
@@ -51,17 +64,13 @@ static void __hmem_register_resource(int target_nid, struct resource *res)
if (platform_initialized)
return;
pdev = platform_device_alloc("hmem_platform", 0);
if (!pdev) {
rc = platform_device_register(&hmem_platform.pdev);
if (rc) {
pr_err_once("failed to register device-dax hmem_platform device\n");
return;
}
rc = platform_device_add(pdev);
if (rc)
platform_device_put(pdev);
else
platform_initialized = true;
platform_initialized = true;
}
void hmem_register_resource(int target_nid, struct resource *res)
+102 -18
View File
@@ -3,6 +3,7 @@
#include <linux/memregion.h>
#include <linux/module.h>
#include <linux/dax.h>
#include "../../cxl/cxl.h"
#include "../bus.h"
static bool region_idle;
@@ -58,21 +59,22 @@ static void release_hmem(void *pdev)
platform_device_unregister(pdev);
}
static int hmem_register_device(struct device *host, int target_nid,
const struct resource *res)
static struct workqueue_struct *dax_hmem_wq;
void dax_hmem_flush_work(void)
{
flush_workqueue(dax_hmem_wq);
}
EXPORT_SYMBOL_FOR_MODULES(dax_hmem_flush_work, "dax_cxl");
static int __hmem_register_device(struct device *host, int target_nid,
const struct resource *res)
{
struct platform_device *pdev;
struct memregion_info info;
long id;
int rc;
if (IS_ENABLED(CONFIG_CXL_REGION) &&
region_intersects(res->start, resource_size(res), IORESOURCE_MEM,
IORES_DESC_CXL) != REGION_DISJOINT) {
dev_dbg(host, "deferring range to CXL: %pr\n", res);
return 0;
}
rc = region_intersects_soft_reserve(res->start, resource_size(res));
if (rc != REGION_INTERSECTS)
return 0;
@@ -94,6 +96,7 @@ static int hmem_register_device(struct device *host, int target_nid,
return -ENOMEM;
}
pdev->dev.parent = host;
pdev->dev.numa_node = numa_map_to_online_node(target_nid);
info = (struct memregion_info) {
.target_node = target_nid,
@@ -123,8 +126,74 @@ out_put:
return rc;
}
static int hmem_register_cxl_device(struct device *host, int target_nid,
const struct resource *res)
{
if (region_intersects(res->start, resource_size(res), IORESOURCE_MEM,
IORES_DESC_CXL) == REGION_DISJOINT)
return 0;
if (cxl_region_contains_resource(res)) {
dev_dbg(host, "CXL claims resource, dropping: %pr\n", res);
return 0;
}
dev_dbg(host, "CXL did not claim resource, registering: %pr\n", res);
return __hmem_register_device(host, target_nid, res);
}
static void process_defer_work(struct work_struct *w)
{
struct hmem_platform_device *hpdev = container_of(w, typeof(*hpdev), work);
struct device *dev = &hpdev->pdev.dev;
/* Relies on cxl_acpi and cxl_pci having had a chance to load */
wait_for_device_probe();
guard(device)(dev);
if (!dev->driver)
goto out;
if (!hpdev->did_probe) {
hpdev->did_probe = true;
walk_hmem_resources(dev, hmem_register_cxl_device);
}
out:
put_device(dev);
}
static int hmem_register_device(struct device *host, int target_nid,
const struct resource *res)
{
struct platform_device *pdev = to_platform_device(host);
struct hmem_platform_device *hpdev = to_hmem_platform_device(pdev);
if (IS_ENABLED(CONFIG_DEV_DAX_CXL) &&
region_intersects(res->start, resource_size(res), IORESOURCE_MEM,
IORES_DESC_CXL) != REGION_DISJOINT) {
if (!hpdev->did_probe) {
dev_dbg(host, "await CXL initial probe: %pr\n", res);
hpdev->work.func = process_defer_work;
get_device(host);
if (!queue_work(dax_hmem_wq, &hpdev->work))
put_device(host);
return 0;
}
dev_dbg(host, "deferring range to CXL: %pr\n", res);
return 0;
}
return __hmem_register_device(host, target_nid, res);
}
static int dax_hmem_platform_probe(struct platform_device *pdev)
{
struct hmem_platform_device *hpdev = to_hmem_platform_device(pdev);
/* queue is only flushed on module unload, fail rebind with pending work */
if (work_pending(&hpdev->work))
return -EBUSY;
return walk_hmem_resources(&pdev->dev, hmem_register_device);
}
@@ -139,13 +208,34 @@ static __init int dax_hmem_init(void)
{
int rc;
/*
* Ensure that cxl_acpi and cxl_pci have a chance to kick off
* CXL topology discovery at least once before scanning the
* iomem resource tree for IORES_DESC_CXL resources.
*/
if (IS_ENABLED(CONFIG_DEV_DAX_CXL)) {
request_module("cxl_acpi");
request_module("cxl_pci");
}
dax_hmem_wq = alloc_ordered_workqueue("dax_hmem_wq", 0);
if (!dax_hmem_wq)
return -ENOMEM;
rc = platform_driver_register(&dax_hmem_platform_driver);
if (rc)
return rc;
goto err_platform_driver;
rc = platform_driver_register(&dax_hmem_driver);
if (rc)
platform_driver_unregister(&dax_hmem_platform_driver);
goto err_driver;
return 0;
err_driver:
platform_driver_unregister(&dax_hmem_platform_driver);
err_platform_driver:
destroy_workqueue(dax_hmem_wq);
return rc;
}
@@ -154,18 +244,12 @@ static __exit void dax_hmem_exit(void)
{
platform_driver_unregister(&dax_hmem_driver);
platform_driver_unregister(&dax_hmem_platform_driver);
destroy_workqueue(dax_hmem_wq);
}
module_init(dax_hmem_init);
module_exit(dax_hmem_exit);
/* Allow for CXL to define its own dax regions */
#if IS_ENABLED(CONFIG_CXL_REGION)
#if IS_MODULE(CONFIG_CXL_ACPI)
MODULE_SOFTDEP("pre: cxl_acpi");
#endif
#endif
MODULE_ALIAS("platform:hmem*");
MODULE_ALIAS("platform:hmem_platform*");
MODULE_DESCRIPTION("HMEM DAX: direct access to 'specific purpose' memory");
+1 -1
View File
@@ -226,7 +226,7 @@ static int __init map_properties(void)
*/
data->len = 0;
memunmap(data);
memblock_free_late(pa_data + sizeof(*data), data_len);
memblock_phys_free(pa_data + sizeof(*data), data_len);
return ret;
}
+1 -2
View File
@@ -1211,8 +1211,7 @@ config I2C_SYNQUACER
config I2C_TEGRA
tristate "NVIDIA Tegra internal I2C controller"
depends on ARCH_TEGRA || (COMPILE_TEST && (ARC || ARM || ARM64 || M68K || RISCV || SUPERH || SPARC))
# COMPILE_TEST needs architectures with readsX()/writesX() primitives
depends on ARCH_TEGRA || COMPILE_TEST
depends on PINCTRL
# ARCH_TEGRA implies PINCTRL, but the COMPILE_TEST side doesn't.
help
+2 -3
View File
@@ -270,8 +270,7 @@ static struct i2c_adapter_quirks cp2615_i2c_quirks = {
.max_comb_2nd_msg_len = MAX_I2C_SIZE
};
static void
cp2615_i2c_remove(struct usb_interface *usbif)
static void cp2615_i2c_disconnect(struct usb_interface *usbif)
{
struct i2c_adapter *adap = usb_get_intfdata(usbif);
@@ -328,7 +327,7 @@ MODULE_DEVICE_TABLE(usb, id_table);
static struct usb_driver cp2615_i2c_driver = {
.name = "i2c-cp2615",
.probe = cp2615_i2c_probe,
.disconnect = cp2615_i2c_remove,
.disconnect = cp2615_i2c_disconnect,
.id_table = id_table,
};
@@ -268,6 +268,7 @@ static const struct acpi_device_id dw_i2c_acpi_match[] = {
{ "AMDI0510", 0 },
{ "APMC0D0F", 0 },
{ "FUJI200B", 0 },
{ "GOOG5000", 0 },
{ "HISI02A1", 0 },
{ "HISI02A2", 0 },
{ "HISI02A3", 0 },
+3 -9
View File
@@ -427,12 +427,6 @@ static const struct usb_device_id diolan_u2c_table[] = {
MODULE_DEVICE_TABLE(usb, diolan_u2c_table);
static void diolan_u2c_free(struct i2c_diolan_u2c *dev)
{
usb_put_dev(dev->usb_dev);
kfree(dev);
}
static int diolan_u2c_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
@@ -453,7 +447,7 @@ static int diolan_u2c_probe(struct usb_interface *interface,
dev->ep_out = hostif->endpoint[0].desc.bEndpointAddress;
dev->ep_in = hostif->endpoint[1].desc.bEndpointAddress;
dev->usb_dev = usb_get_dev(interface_to_usbdev(interface));
dev->usb_dev = interface_to_usbdev(interface);
dev->interface = interface;
/* save our data pointer in this interface device */
@@ -488,7 +482,7 @@ static int diolan_u2c_probe(struct usb_interface *interface,
error_free:
usb_set_intfdata(interface, NULL);
diolan_u2c_free(dev);
kfree(dev);
error:
return ret;
}
@@ -499,7 +493,7 @@ static void diolan_u2c_disconnect(struct usb_interface *interface)
i2c_del_adapter(&dev->adapter);
usb_set_intfdata(interface, NULL);
diolan_u2c_free(dev);
kfree(dev);
dev_dbg(&interface->dev, "disconnected\n");
}
+235 -79
View File
@@ -98,6 +98,10 @@
#define SPACEMIT_BUS_RESET_CLK_CNT_MAX 9
#define SPACEMIT_WAIT_TIMEOUT 1000 /* ms */
#define SPACEMIT_POLL_TIMEOUT 1000 /* us */
#define SPACEMIT_POLL_INTERVAL 30 /* us */
enum spacemit_i2c_state {
SPACEMIT_STATE_IDLE,
SPACEMIT_STATE_START,
@@ -126,6 +130,7 @@ struct spacemit_i2c_dev {
enum spacemit_i2c_state state;
bool read;
bool use_pio;
struct completion complete;
u32 status;
};
@@ -172,6 +177,14 @@ static int spacemit_i2c_handle_err(struct spacemit_i2c_dev *i2c)
return i2c->status & SPACEMIT_SR_ACKNAK ? -ENXIO : -EIO;
}
static inline void spacemit_i2c_delay(struct spacemit_i2c_dev *i2c, unsigned int us)
{
if (i2c->use_pio)
udelay(us);
else
fsleep(us);
}
static void spacemit_i2c_conditionally_reset_bus(struct spacemit_i2c_dev *i2c)
{
u32 status;
@@ -183,7 +196,8 @@ static void spacemit_i2c_conditionally_reset_bus(struct spacemit_i2c_dev *i2c)
return;
spacemit_i2c_reset(i2c);
usleep_range(10, 20);
spacemit_i2c_delay(i2c, 10);
for (clk_cnt = 0; clk_cnt < SPACEMIT_BUS_RESET_CLK_CNT_MAX; clk_cnt++) {
status = readl(i2c->base + SPACEMIT_IBMR);
@@ -212,9 +226,15 @@ static int spacemit_i2c_wait_bus_idle(struct spacemit_i2c_dev *i2c)
if (!(val & (SPACEMIT_SR_UB | SPACEMIT_SR_IBB)))
return 0;
ret = readl_poll_timeout(i2c->base + SPACEMIT_ISR,
val, !(val & (SPACEMIT_SR_UB | SPACEMIT_SR_IBB)),
1500, SPACEMIT_I2C_BUS_BUSY_TIMEOUT);
if (i2c->use_pio)
ret = readl_poll_timeout_atomic(i2c->base + SPACEMIT_ISR,
val, !(val & (SPACEMIT_SR_UB | SPACEMIT_SR_IBB)),
1500, SPACEMIT_I2C_BUS_BUSY_TIMEOUT);
else
ret = readl_poll_timeout(i2c->base + SPACEMIT_ISR,
val, !(val & (SPACEMIT_SR_UB | SPACEMIT_SR_IBB)),
1500, SPACEMIT_I2C_BUS_BUSY_TIMEOUT);
if (ret)
spacemit_i2c_reset(i2c);
@@ -226,7 +246,7 @@ static void spacemit_i2c_check_bus_release(struct spacemit_i2c_dev *i2c)
/* in case bus is not released after transfer completes */
if (readl(i2c->base + SPACEMIT_ISR) & SPACEMIT_SR_EBB) {
spacemit_i2c_conditionally_reset_bus(i2c);
usleep_range(90, 150);
spacemit_i2c_delay(i2c, 90);
}
}
@@ -238,25 +258,33 @@ spacemit_i2c_clear_int_status(struct spacemit_i2c_dev *i2c, u32 mask)
static void spacemit_i2c_init(struct spacemit_i2c_dev *i2c)
{
u32 val;
u32 val = 0;
/*
* Unmask interrupt bits for all xfer mode:
* bus error, arbitration loss detected.
* For transaction complete signal, we use master stop
* interrupt, so we don't need to unmask SPACEMIT_CR_TXDONEIE.
*/
val = SPACEMIT_CR_BEIE | SPACEMIT_CR_ALDIE;
if (!i2c->use_pio) {
/*
* Enable interrupt bits for all xfer mode:
* bus error, arbitration loss detected.
*/
val |= SPACEMIT_CR_BEIE | SPACEMIT_CR_ALDIE;
/*
* Unmask interrupt bits for interrupt xfer mode:
* When IDBR receives a byte, an interrupt is triggered.
*
* For the tx empty interrupt, it will be enabled in the
* i2c_start function.
* Otherwise, it will cause an erroneous empty interrupt before i2c_start.
*/
val |= SPACEMIT_CR_DRFIE;
/*
* Unmask interrupt bits for interrupt xfer mode:
* When IDBR receives a byte, an interrupt is triggered.
*
* For the tx empty interrupt, it will be enabled in the
* i2c_start().
* We don't want a TX empty interrupt until we start
* a transfer in i2c_start().
*/
val |= SPACEMIT_CR_DRFIE;
/*
* Enable master stop interrupt bit.
* For transaction complete signal, we use master stop
* interrupt, so we don't need to unmask SPACEMIT_CR_TXDONEIE.
*/
val |= SPACEMIT_CR_MSDIE;
}
if (i2c->clock_freq == SPACEMIT_I2C_MAX_FAST_MODE_FREQ)
val |= SPACEMIT_CR_MODE_FAST;
@@ -268,7 +296,7 @@ static void spacemit_i2c_init(struct spacemit_i2c_dev *i2c)
val |= SPACEMIT_CR_SCLE;
/* enable master stop detected */
val |= SPACEMIT_CR_MSDE | SPACEMIT_CR_MSDIE;
val |= SPACEMIT_CR_MSDE;
writel(val, i2c->base + SPACEMIT_ICR);
@@ -301,41 +329,15 @@ static void spacemit_i2c_start(struct spacemit_i2c_dev *i2c)
/* send start pulse */
val = readl(i2c->base + SPACEMIT_ICR);
val &= ~SPACEMIT_CR_STOP;
val |= SPACEMIT_CR_START | SPACEMIT_CR_TB | SPACEMIT_CR_DTEIE;
val |= SPACEMIT_CR_START | SPACEMIT_CR_TB;
/* Enable the TX empty interrupt */
if (!i2c->use_pio)
val |= SPACEMIT_CR_DTEIE;
writel(val, i2c->base + SPACEMIT_ICR);
}
static int spacemit_i2c_xfer_msg(struct spacemit_i2c_dev *i2c)
{
unsigned long time_left;
struct i2c_msg *msg;
for (i2c->msg_idx = 0; i2c->msg_idx < i2c->msg_num; i2c->msg_idx++) {
msg = &i2c->msgs[i2c->msg_idx];
i2c->msg_buf = msg->buf;
i2c->unprocessed = msg->len;
i2c->status = 0;
reinit_completion(&i2c->complete);
spacemit_i2c_start(i2c);
time_left = wait_for_completion_timeout(&i2c->complete,
i2c->adapt.timeout);
if (!time_left) {
dev_err(i2c->dev, "msg completion timeout\n");
spacemit_i2c_conditionally_reset_bus(i2c);
spacemit_i2c_reset(i2c);
return -ETIMEDOUT;
}
if (i2c->status & SPACEMIT_SR_ERR)
return spacemit_i2c_handle_err(i2c);
}
return 0;
}
static bool spacemit_i2c_is_last_msg(struct spacemit_i2c_dev *i2c)
{
if (i2c->msg_idx != i2c->msg_num - 1)
@@ -347,8 +349,23 @@ static bool spacemit_i2c_is_last_msg(struct spacemit_i2c_dev *i2c)
return !i2c->unprocessed;
}
static inline void spacemit_i2c_complete(struct spacemit_i2c_dev *i2c)
{
/* SPACEMIT_STATE_IDLE avoids triggering the next byte */
i2c->state = SPACEMIT_STATE_IDLE;
if (i2c->use_pio)
return;
complete(&i2c->complete);
}
static void spacemit_i2c_handle_write(struct spacemit_i2c_dev *i2c)
{
/* If there's no space in the IDBR, we're done */
if (!(i2c->status & SPACEMIT_SR_ITE))
return;
/* if transfer completes, SPACEMIT_ISR will handle it */
if (i2c->status & SPACEMIT_SR_MSD)
return;
@@ -359,16 +376,19 @@ static void spacemit_i2c_handle_write(struct spacemit_i2c_dev *i2c)
return;
}
/* SPACEMIT_STATE_IDLE avoids trigger next byte */
i2c->state = SPACEMIT_STATE_IDLE;
complete(&i2c->complete);
spacemit_i2c_complete(i2c);
}
static void spacemit_i2c_handle_read(struct spacemit_i2c_dev *i2c)
{
/* If there's nothing in the IDBR, we're done */
if (!(i2c->status & SPACEMIT_SR_IRF))
return;
if (i2c->unprocessed) {
*i2c->msg_buf++ = readl(i2c->base + SPACEMIT_IDBR);
i2c->unprocessed--;
return;
}
/* if transfer completes, SPACEMIT_ISR will handle it */
@@ -379,9 +399,7 @@ static void spacemit_i2c_handle_read(struct spacemit_i2c_dev *i2c)
if (i2c->unprocessed)
return;
/* SPACEMIT_STATE_IDLE avoids trigger next byte */
i2c->state = SPACEMIT_STATE_IDLE;
complete(&i2c->complete);
spacemit_i2c_complete(i2c);
}
static void spacemit_i2c_handle_start(struct spacemit_i2c_dev *i2c)
@@ -415,29 +433,16 @@ static void spacemit_i2c_err_check(struct spacemit_i2c_dev *i2c)
spacemit_i2c_clear_int_status(i2c, SPACEMIT_I2C_INT_STATUS_MASK);
i2c->state = SPACEMIT_STATE_IDLE;
complete(&i2c->complete);
spacemit_i2c_complete(i2c);
}
static irqreturn_t spacemit_i2c_irq_handler(int irq, void *devid)
static void spacemit_i2c_handle_state(struct spacemit_i2c_dev *i2c)
{
struct spacemit_i2c_dev *i2c = devid;
u32 status, val;
status = readl(i2c->base + SPACEMIT_ISR);
if (!status)
return IRQ_HANDLED;
i2c->status = status;
spacemit_i2c_clear_int_status(i2c, status);
u32 val;
if (i2c->status & SPACEMIT_SR_ERR)
goto err_out;
val = readl(i2c->base + SPACEMIT_ICR);
val &= ~(SPACEMIT_CR_TB | SPACEMIT_CR_ACKNAK | SPACEMIT_CR_STOP | SPACEMIT_CR_START);
switch (i2c->state) {
case SPACEMIT_STATE_START:
spacemit_i2c_handle_start(i2c);
@@ -453,7 +458,12 @@ static irqreturn_t spacemit_i2c_irq_handler(int irq, void *devid)
}
if (i2c->state != SPACEMIT_STATE_IDLE) {
val |= SPACEMIT_CR_TB | SPACEMIT_CR_ALDIE;
val = readl(i2c->base + SPACEMIT_ICR);
val &= ~(SPACEMIT_CR_TB | SPACEMIT_CR_ACKNAK |
SPACEMIT_CR_STOP | SPACEMIT_CR_START);
val |= SPACEMIT_CR_TB;
if (!i2c->use_pio)
val |= SPACEMIT_CR_ALDIE;
if (spacemit_i2c_is_last_msg(i2c)) {
/* trigger next byte with stop */
@@ -467,6 +477,133 @@ static irqreturn_t spacemit_i2c_irq_handler(int irq, void *devid)
err_out:
spacemit_i2c_err_check(i2c);
}
/*
* In PIO mode, this function is used as a replacement for
* wait_for_completion_timeout(), whose return value indicates
* the remaining time.
*
* We do not have a meaningful remaining-time value here, so
* return a non-zero value on success to indicate "not timed out".
* Returning 1 ensures callers treating the return value as
* time_left will not incorrectly report a timeout.
*/
static int spacemit_i2c_wait_pio_xfer(struct spacemit_i2c_dev *i2c)
{
u32 mask, msec = jiffies_to_msecs(i2c->adapt.timeout);
ktime_t timeout = ktime_add_ms(ktime_get(), msec);
int ret;
mask = SPACEMIT_SR_IRF | SPACEMIT_SR_ITE;
do {
i2c->status = readl(i2c->base + SPACEMIT_ISR);
spacemit_i2c_clear_int_status(i2c, i2c->status);
if (i2c->status & mask)
spacemit_i2c_handle_state(i2c);
else
udelay(SPACEMIT_POLL_INTERVAL);
} while (i2c->unprocessed && ktime_compare(ktime_get(), timeout) < 0);
if (i2c->unprocessed)
return 0;
if (i2c->read)
return 1;
/*
* If this is the last byte to write of the current message,
* we have to wait here. Otherwise, control will proceed directly
* to start(), which would overwrite the current data.
*/
ret = readl_poll_timeout_atomic(i2c->base + SPACEMIT_ISR,
i2c->status, i2c->status & SPACEMIT_SR_ITE,
SPACEMIT_POLL_INTERVAL, SPACEMIT_POLL_TIMEOUT);
if (ret)
return 0;
/*
* For writes: in interrupt mode, an ITE (write-empty) interrupt is triggered
* after the last byte, and the MSD-related handling takes place there.
* In PIO mode, however, we need to explicitly call err_check() to emulate this
* step, otherwise the next transfer will fail.
*/
if (i2c->msg_idx == i2c->msg_num - 1) {
mask = SPACEMIT_SR_MSD | SPACEMIT_SR_ERR;
/*
* In some cases, MSD may not arrive immediately;
* wait here to handle that.
*/
ret = readl_poll_timeout_atomic(i2c->base + SPACEMIT_ISR,
i2c->status, i2c->status & mask,
SPACEMIT_POLL_INTERVAL, SPACEMIT_POLL_TIMEOUT);
if (ret)
return 0;
spacemit_i2c_err_check(i2c);
}
return 1;
}
static int spacemit_i2c_wait_xfer_complete(struct spacemit_i2c_dev *i2c)
{
if (i2c->use_pio)
return spacemit_i2c_wait_pio_xfer(i2c);
return wait_for_completion_timeout(&i2c->complete,
i2c->adapt.timeout);
}
static int spacemit_i2c_xfer_msg(struct spacemit_i2c_dev *i2c)
{
unsigned long time_left;
struct i2c_msg *msg;
for (i2c->msg_idx = 0; i2c->msg_idx < i2c->msg_num; i2c->msg_idx++) {
msg = &i2c->msgs[i2c->msg_idx];
i2c->msg_buf = msg->buf;
i2c->unprocessed = msg->len;
i2c->status = 0;
reinit_completion(&i2c->complete);
spacemit_i2c_start(i2c);
time_left = spacemit_i2c_wait_xfer_complete(i2c);
if (!time_left) {
dev_err(i2c->dev, "msg completion timeout\n");
spacemit_i2c_conditionally_reset_bus(i2c);
spacemit_i2c_reset(i2c);
return -ETIMEDOUT;
}
if (i2c->status & SPACEMIT_SR_ERR)
return spacemit_i2c_handle_err(i2c);
}
return 0;
}
static irqreturn_t spacemit_i2c_irq_handler(int irq, void *devid)
{
struct spacemit_i2c_dev *i2c = devid;
u32 status;
status = readl(i2c->base + SPACEMIT_ISR);
if (!status)
return IRQ_HANDLED;
i2c->status = status;
spacemit_i2c_clear_int_status(i2c, status);
spacemit_i2c_handle_state(i2c);
return IRQ_HANDLED;
}
@@ -475,6 +612,11 @@ static void spacemit_i2c_calc_timeout(struct spacemit_i2c_dev *i2c)
unsigned long timeout;
int idx = 0, cnt = 0;
if (i2c->use_pio) {
i2c->adapt.timeout = msecs_to_jiffies(SPACEMIT_WAIT_TIMEOUT);
return;
}
for (; idx < i2c->msg_num; idx++)
cnt += (i2c->msgs + idx)->len + 1;
@@ -487,11 +629,14 @@ static void spacemit_i2c_calc_timeout(struct spacemit_i2c_dev *i2c)
i2c->adapt.timeout = usecs_to_jiffies(timeout + USEC_PER_SEC / 10) / i2c->msg_num;
}
static int spacemit_i2c_xfer(struct i2c_adapter *adapt, struct i2c_msg *msgs, int num)
static inline int
spacemit_i2c_xfer_common(struct i2c_adapter *adapt, struct i2c_msg *msgs, int num, bool use_pio)
{
struct spacemit_i2c_dev *i2c = i2c_get_adapdata(adapt);
int ret;
i2c->use_pio = use_pio;
i2c->msgs = msgs;
i2c->msg_num = num;
@@ -519,6 +664,16 @@ static int spacemit_i2c_xfer(struct i2c_adapter *adapt, struct i2c_msg *msgs, in
return ret < 0 ? ret : num;
}
static int spacemit_i2c_xfer(struct i2c_adapter *adapt, struct i2c_msg *msgs, int num)
{
return spacemit_i2c_xfer_common(adapt, msgs, num, false);
}
static int spacemit_i2c_pio_xfer_atomic(struct i2c_adapter *adapt, struct i2c_msg *msgs, int num)
{
return spacemit_i2c_xfer_common(adapt, msgs, num, true);
}
static u32 spacemit_i2c_func(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
@@ -526,6 +681,7 @@ static u32 spacemit_i2c_func(struct i2c_adapter *adap)
static const struct i2c_algorithm spacemit_i2c_algo = {
.xfer = spacemit_i2c_xfer,
.xfer_atomic = spacemit_i2c_pio_xfer_atomic,
.functionality = spacemit_i2c_func,
};
+1 -1
View File
@@ -1384,7 +1384,7 @@ static irqreturn_t npcm_i2c_int_slave_handler(struct npcm_i2c *bus)
*/
bus->operation = I2C_NO_OPER;
bus->own_slave_addr = 0xFF;
i2c_slave_event(bus->slave, I2C_SLAVE_STOP, 0);
i2c_slave_event(bus->slave, I2C_SLAVE_STOP, NULL);
iowrite8(NPCM_I2CST_SLVSTP, bus->reg + NPCM_I2CST);
if (bus->fifo_use) {
npcm_i2c_clear_fifo_int(bus);
+9 -15
View File
@@ -24,6 +24,7 @@
#include <linux/io.h>
#include <linux/log2.h>
#include <linux/spinlock.h>
#include <linux/iopoll.h>
#include <linux/jiffies.h>
/*
@@ -258,7 +259,7 @@ static void ocores_process_timeout(struct ocores_i2c *i2c)
* @reg: register to query
* @mask: bitmask to apply on register value
* @val: expected result
* @timeout: timeout in jiffies
* @timeout_us: timeout in microseconds
*
* Timeout is necessary to avoid to stay here forever when the chip
* does not answer correctly.
@@ -267,21 +268,14 @@ static void ocores_process_timeout(struct ocores_i2c *i2c)
*/
static int ocores_wait(struct ocores_i2c *i2c,
int reg, u8 mask, u8 val,
const unsigned long timeout)
unsigned long timeout_us)
{
unsigned long j;
u8 status;
j = jiffies + timeout;
while (1) {
u8 status = oc_getreg(i2c, reg);
if ((status & mask) == val)
break;
if (time_after(jiffies, j))
return -ETIMEDOUT;
}
return 0;
return read_poll_timeout_atomic(oc_getreg, status,
(status & mask) == val,
0, timeout_us, false,
i2c, reg);
}
/**
@@ -314,7 +308,7 @@ static int ocores_poll_wait(struct ocores_i2c *i2c)
* once we are here we expect to get the expected result immediately
* so if after 1ms we timeout then something is broken.
*/
err = ocores_wait(i2c, OCI2C_STATUS, mask, 0, msecs_to_jiffies(1));
err = ocores_wait(i2c, OCI2C_STATUS, mask, 0, 1000);
if (err)
dev_warn(i2c->adap.dev.parent,
"%s: STATUS timeout, bit 0x%x did not clear in 1ms\n",
-3
View File
@@ -71,9 +71,6 @@
#define NUM_MASTERS 2
#define NUM_QUEUES 2
/* Max number of resources + 1 for a NULL terminator */
#define CCI_RES_MAX 6
#define CCI_I2C_SET_PARAM 1
#define CCI_I2C_REPORT 8
#define CCI_I2C_WRITE 9
+19 -5
View File
@@ -625,8 +625,8 @@ static int geni_i2c_gpi(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[],
{
struct gpi_i2c_config *peripheral;
unsigned int flags;
void *dma_buf;
dma_addr_t addr;
void *dma_buf = NULL;
dma_addr_t addr = 0;
enum dma_data_direction map_dirn;
enum dma_transfer_direction dma_dirn;
struct dma_async_tx_descriptor *desc;
@@ -639,6 +639,16 @@ static int geni_i2c_gpi(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[],
gi2c_gpi_xfer = &gi2c->i2c_multi_desc_config;
msg_idx = gi2c_gpi_xfer->msg_idx_cnt;
/*
* Skip TX DMA mapping for a read message (I2C_M_RD) to avoid
* programming an extra TX DMA TRE that would cause an unintended
* write cycle on the I2C bus before the actual read operation.
*/
if (op == I2C_WRITE && msgs[msg_idx].flags & I2C_M_RD) {
peripheral->multi_msg = true;
goto skip_tx_dma_map;
}
dma_buf = i2c_get_dma_safe_msg_buf(&msgs[msg_idx], 1);
if (!dma_buf) {
ret = -ENOMEM;
@@ -658,6 +668,7 @@ static int geni_i2c_gpi(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[],
goto out;
}
skip_tx_dma_map:
if (gi2c->is_tx_multi_desc_xfer) {
flags = DMA_CTRL_ACK;
@@ -740,9 +751,12 @@ static int geni_i2c_gpi(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[],
return 0;
err_config:
dma_unmap_single(gi2c->se.dev->parent, addr,
msgs[msg_idx].len, map_dirn);
i2c_put_dma_safe_msg_buf(dma_buf, &msgs[msg_idx], false);
/* Avoid DMA unmap as the write operation skipped DMA mapping */
if (dma_buf) {
dma_unmap_single(gi2c->se.dev->parent, addr,
msgs[msg_idx].len, map_dirn);
i2c_put_dma_safe_msg_buf(dma_buf, &msgs[msg_idx], false);
}
out:
gi2c->err = ret;
+1 -3
View File
@@ -141,7 +141,7 @@ static int osif_probe(struct usb_interface *interface,
if (!priv)
return -ENOMEM;
priv->usb_dev = usb_get_dev(interface_to_usbdev(interface));
priv->usb_dev = interface_to_usbdev(interface);
priv->interface = interface;
usb_set_intfdata(interface, priv);
@@ -163,7 +163,6 @@ static int osif_probe(struct usb_interface *interface,
NULL, 0);
if (ret) {
dev_err(&interface->dev, "failure sending bit rate");
usb_put_dev(priv->usb_dev);
return ret;
}
@@ -184,7 +183,6 @@ static void osif_disconnect(struct usb_interface *interface)
i2c_del_adapter(&(priv->adapter));
usb_set_intfdata(interface, NULL);
usb_put_dev(priv->usb_dev);
}
static struct usb_driver osif_driver = {
+164 -31
View File
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/i2c.h>
#include <linux/i2c-mux.h>
#include <linux/mod_devicetable.h>
@@ -11,10 +12,16 @@
#include <linux/unaligned.h>
enum rtl9300_bus_freq {
RTL9300_I2C_STD_FREQ,
RTL9300_I2C_FAST_FREQ,
RTL9300_I2C_STD_FREQ, // 100kHz
RTL9300_I2C_FAST_FREQ, // 400kHz
RTL9300_I2C_SUPER_FAST_FREQ, // 2.5MHz
RTL9300_I2C_SLOW_FREQ, // 50kHz
};
#define RTL9300_I2C_MAX_SUPER_FAST_FREQ 2500000
#define RTL9300_I2C_MAX_SLOW_FREQ 50000
struct rtl9300_i2c;
struct rtl9300_i2c_chan {
@@ -22,6 +29,7 @@ struct rtl9300_i2c_chan {
struct rtl9300_i2c *i2c;
enum rtl9300_bus_freq bus_freq;
u8 sda_num;
u32 clk_div;
};
enum rtl9300_i2c_reg_scope {
@@ -47,6 +55,9 @@ enum rtl9300_i2c_reg_fields {
F_SCL_SEL,
F_SDA_OUT_SEL,
F_SDA_SEL,
F_BUSY,
F_CLK_DIV,
F_EXT_SCK_5MS,
/* keep last */
F_NUM_FIELDS
@@ -55,12 +66,22 @@ enum rtl9300_i2c_reg_fields {
struct rtl9300_i2c_drv_data {
struct rtl9300_i2c_reg_field field_desc[F_NUM_FIELDS];
int (*select_scl)(struct rtl9300_i2c *i2c, u8 scl);
u32 data_reg;
int (*config_chan)(struct rtl9300_i2c *i2c, struct rtl9300_i2c_chan *chan);
void (*config_clock)(u32 clock_freq, struct rtl9300_i2c_chan *chan);
int (*misc_init)(struct rtl9300_i2c *i2c);
u32 rd_reg;
u32 wd_reg;
u8 max_nchan;
u8 max_data_len;
u8 reg_addr_8bit_len;
};
#define RTL9300_I2C_MUX_NCHAN 8
#define RTL9310_I2C_MUX_NCHAN 12
#define RTL9607_I2C_MUX_NCHAN 1
#define RTL9300_I2C_MAX_DATA_LEN 16
#define RTL9607_I2C_MAX_DATA_LEN 4
struct rtl9300_i2c {
struct regmap *regmap;
@@ -68,10 +89,12 @@ struct rtl9300_i2c {
struct rtl9300_i2c_chan chans[RTL9310_I2C_MUX_NCHAN];
struct regmap_field *fields[F_NUM_FIELDS];
u32 reg_base;
u32 data_reg;
u32 rd_reg;
u32 wd_reg;
u8 scl_num;
u8 sda_num;
struct mutex lock;
struct clk *clk;
};
DEFINE_GUARD(rtl9300_i2c, struct rtl9300_i2c *, mutex_lock(&_T->lock), mutex_unlock(&_T->lock))
@@ -99,6 +122,7 @@ struct rtl9300_i2c_xfer {
#define RTL9300_I2C_MST_DATA_WORD2 0x10
#define RTL9300_I2C_MST_DATA_WORD3 0x14
#define RTL9300_I2C_MST_GLB_CTRL 0x384
#define RTL9300_REG_ADDR_8BIT_LEN 1
#define RTL9310_I2C_MST_IF_CTRL 0x1004
#define RTL9310_I2C_MST_IF_SEL 0x1008
@@ -106,6 +130,14 @@ struct rtl9300_i2c_xfer {
#define RTL9310_I2C_MST_MEMADDR_CTRL 0x4
#define RTL9310_I2C_MST_DATA_CTRL 0x8
#define RTL9607_I2C_CONFIG 0x22f50
#define RTL9607_IO_MODE_EN 0x23014
#define RTL9607_I2C_IND_WD 0x0
#define RTL9607_I2C_IND_ADR 0x8
#define RTL9607_I2C_IND_CMD 0x10
#define RTL9607_I2C_IND_RD 0x18
#define RTL9607_REG_ADDR_8BIT_LEN 0
static int rtl9300_i2c_reg_addr_set(struct rtl9300_i2c *i2c, u32 reg, u16 len)
{
int ret;
@@ -157,6 +189,58 @@ static int rtl9300_i2c_config_chan(struct rtl9300_i2c *i2c, struct rtl9300_i2c_c
return 0;
}
static int rtl9607_i2c_config_chan(struct rtl9300_i2c *i2c, struct rtl9300_i2c_chan *chan)
{
const struct rtl9300_i2c_drv_data *drv_data;
int ret;
if (i2c->sda_num == chan->sda_num)
return 0;
ret = regmap_field_write(i2c->fields[F_CLK_DIV], chan->clk_div);
if (ret)
return ret;
drv_data = device_get_match_data(i2c->dev);
ret = drv_data->select_scl(i2c, i2c->scl_num);
if (ret)
return ret;
i2c->sda_num = chan->sda_num;
return 0;
}
static void rtl9300_i2c_config_clock(u32 clock_freq, struct rtl9300_i2c_chan *chan)
{
struct rtl9300_i2c *i2c = chan->i2c;
switch (clock_freq) {
case I2C_MAX_STANDARD_MODE_FREQ:
chan->bus_freq = RTL9300_I2C_STD_FREQ;
break;
case I2C_MAX_FAST_MODE_FREQ:
chan->bus_freq = RTL9300_I2C_FAST_FREQ;
break;
case RTL9300_I2C_MAX_SUPER_FAST_FREQ:
chan->bus_freq = RTL9300_I2C_SUPER_FAST_FREQ;
break;
case RTL9300_I2C_MAX_SLOW_FREQ:
chan->bus_freq = RTL9300_I2C_SLOW_FREQ;
break;
default:
dev_warn(i2c->dev, "SDA%d clock-frequency %d not supported using default\n",
chan->sda_num, clock_freq);
break;
}
}
static void rtl9607_i2c_config_clock(u32 clock_freq, struct rtl9300_i2c_chan *chan)
{
struct rtl9300_i2c *i2c = chan->i2c;
chan->clk_div = clk_get_rate(i2c->clk) / clock_freq - 1;
}
static int rtl9300_i2c_read(struct rtl9300_i2c *i2c, u8 *buf, u8 len)
{
u32 vals[4] = {};
@@ -165,7 +249,7 @@ static int rtl9300_i2c_read(struct rtl9300_i2c *i2c, u8 *buf, u8 len)
if (len > 16)
return -EIO;
ret = regmap_bulk_read(i2c->regmap, i2c->data_reg, vals, ARRAY_SIZE(vals));
ret = regmap_bulk_read(i2c->regmap, i2c->rd_reg, vals, ARRAY_SIZE(vals));
if (ret)
return ret;
@@ -192,19 +276,21 @@ static int rtl9300_i2c_write(struct rtl9300_i2c *i2c, u8 *buf, u8 len)
vals[reg] |= buf[i] << shift;
}
return regmap_bulk_write(i2c->regmap, i2c->data_reg, vals, ARRAY_SIZE(vals));
return regmap_bulk_write(i2c->regmap, i2c->wd_reg, vals, ARRAY_SIZE(vals));
}
static int rtl9300_i2c_writel(struct rtl9300_i2c *i2c, u32 data)
{
return regmap_write(i2c->regmap, i2c->data_reg, data);
return regmap_write(i2c->regmap, i2c->wd_reg, data);
}
static int rtl9300_i2c_prepare_xfer(struct rtl9300_i2c *i2c, struct rtl9300_i2c_xfer *xfer)
{
const struct rtl9300_i2c_drv_data *drv_data;
int ret;
if (xfer->data_len < 1 || xfer->data_len > 16)
drv_data = device_get_match_data(i2c->dev);
if (xfer->data_len < 1 || xfer->data_len > drv_data->max_data_len)
return -EINVAL;
ret = regmap_field_write(i2c->fields[F_DEV_ADDR], xfer->dev_addr);
@@ -249,7 +335,7 @@ static int rtl9300_i2c_do_xfer(struct rtl9300_i2c *i2c, struct rtl9300_i2c_xfer
if (ret)
return ret;
ret = regmap_field_read_poll_timeout(i2c->fields[F_I2C_TRIG], val, !val, 100, 100000);
ret = regmap_field_read_poll_timeout(i2c->fields[F_BUSY], val, !val, 100, 100000);
if (ret)
return ret;
@@ -262,14 +348,14 @@ static int rtl9300_i2c_do_xfer(struct rtl9300_i2c *i2c, struct rtl9300_i2c_xfer
if (!xfer->write) {
switch (xfer->type) {
case RTL9300_I2C_XFER_BYTE:
ret = regmap_read(i2c->regmap, i2c->data_reg, &val);
ret = regmap_read(i2c->regmap, i2c->rd_reg, &val);
if (ret)
return ret;
*xfer->data = val & 0xff;
break;
case RTL9300_I2C_XFER_WORD:
ret = regmap_read(i2c->regmap, i2c->data_reg, &val);
ret = regmap_read(i2c->regmap, i2c->rd_reg, &val);
if (ret)
return ret;
@@ -291,6 +377,7 @@ static int rtl9300_i2c_smbus_xfer(struct i2c_adapter *adap, u16 addr, unsigned s
union i2c_smbus_data *data)
{
struct rtl9300_i2c_chan *chan = i2c_get_adapdata(adap);
const struct rtl9300_i2c_drv_data *drv_data;
struct rtl9300_i2c *i2c = chan->i2c;
struct rtl9300_i2c_xfer xfer = {0};
int ret;
@@ -300,14 +387,15 @@ static int rtl9300_i2c_smbus_xfer(struct i2c_adapter *adap, u16 addr, unsigned s
guard(rtl9300_i2c)(i2c);
ret = rtl9300_i2c_config_chan(i2c, chan);
drv_data = device_get_match_data(i2c->dev);
ret = drv_data->config_chan(i2c, chan);
if (ret)
return ret;
xfer.dev_addr = addr & 0x7f;
xfer.write = (read_write == I2C_SMBUS_WRITE);
xfer.reg_addr = command;
xfer.reg_addr_len = 1;
xfer.reg_addr_len = drv_data->reg_addr_8bit_len;
switch (size) {
case I2C_SMBUS_BYTE:
@@ -367,6 +455,17 @@ static struct i2c_adapter_quirks rtl9300_i2c_quirks = {
.max_write_len = 16,
};
static int rtl9300_i2c_init(struct rtl9300_i2c *i2c)
{
/* only use standard read format */
return regmap_field_write(i2c->fields[F_RD_MODE], 0);
}
static int rtl9607_i2c_init(struct rtl9300_i2c *i2c)
{
return regmap_field_write(i2c->fields[F_EXT_SCK_5MS], 1);
}
static int rtl9300_i2c_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -402,7 +501,8 @@ static int rtl9300_i2c_probe(struct platform_device *pdev)
if (device_get_child_node_count(dev) > drv_data->max_nchan)
return dev_err_probe(dev, -EINVAL, "Too many channels\n");
i2c->data_reg = i2c->reg_base + drv_data->data_reg;
i2c->rd_reg = i2c->reg_base + drv_data->rd_reg;
i2c->wd_reg = i2c->reg_base + drv_data->wd_reg;
for (i = 0; i < F_NUM_FIELDS; i++) {
fields[i] = drv_data->field_desc[i].field;
if (drv_data->field_desc[i].scope == REG_SCOPE_MASTER)
@@ -413,6 +513,10 @@ static int rtl9300_i2c_probe(struct platform_device *pdev)
if (ret)
return ret;
i2c->clk = devm_clk_get_optional_enabled(dev, NULL);
if (IS_ERR(i2c->clk))
return dev_err_probe(dev, PTR_ERR(i2c->clk), "Failed to enable i2c clock\n");
i = 0;
for_each_child_of_node_scoped(dev->of_node, child) {
struct rtl9300_i2c_chan *chan = &i2c->chans[i];
@@ -426,21 +530,11 @@ static int rtl9300_i2c_probe(struct platform_device *pdev)
if (ret)
clock_freq = I2C_MAX_STANDARD_MODE_FREQ;
switch (clock_freq) {
case I2C_MAX_STANDARD_MODE_FREQ:
chan->bus_freq = RTL9300_I2C_STD_FREQ;
break;
case I2C_MAX_FAST_MODE_FREQ:
chan->bus_freq = RTL9300_I2C_FAST_FREQ;
break;
default:
dev_warn(i2c->dev, "SDA%d clock-frequency %d not supported using default\n",
sda_num, clock_freq);
break;
}
chan->sda_num = sda_num;
chan->i2c = i2c;
drv_data->config_clock(clock_freq, chan);
adap = &i2c->chans[i].adap;
adap->owner = THIS_MODULE;
adap->algo = &rtl9300_i2c_algo;
@@ -458,8 +552,7 @@ static int rtl9300_i2c_probe(struct platform_device *pdev)
}
i2c->sda_num = 0xff;
/* only use standard read format */
ret = regmap_field_write(i2c->fields[F_RD_MODE], 0);
ret = drv_data->misc_init(i2c);
if (ret)
return ret;
@@ -485,10 +578,17 @@ static const struct rtl9300_i2c_drv_data rtl9300_i2c_drv_data = {
[F_MEM_ADDR_WIDTH] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL2, 2, 3),
[F_SCL_FREQ] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL2, 0, 1),
[F_SDA_SEL] = GLB_REG_FIELD(RTL9300_I2C_MST_GLB_CTRL, 0, 7),
[F_BUSY] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL1, 0, 0),
},
.select_scl = rtl9300_i2c_select_scl,
.data_reg = RTL9300_I2C_MST_DATA_WORD0,
.config_chan = rtl9300_i2c_config_chan,
.config_clock = rtl9300_i2c_config_clock,
.misc_init = rtl9300_i2c_init,
.rd_reg = RTL9300_I2C_MST_DATA_WORD0,
.wd_reg = RTL9300_I2C_MST_DATA_WORD0,
.max_nchan = RTL9300_I2C_MUX_NCHAN,
.max_data_len = RTL9300_I2C_MAX_DATA_LEN,
.reg_addr_8bit_len = RTL9300_REG_ADDR_8BIT_LEN,
};
static const struct rtl9300_i2c_drv_data rtl9310_i2c_drv_data = {
@@ -505,10 +605,42 @@ static const struct rtl9300_i2c_drv_data rtl9310_i2c_drv_data = {
[F_I2C_FAIL] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 1, 1),
[F_I2C_TRIG] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 0, 0),
[F_MEM_ADDR] = MST_REG_FIELD(RTL9310_I2C_MST_MEMADDR_CTRL, 0, 23),
[F_BUSY] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 0, 0),
},
.select_scl = rtl9310_i2c_select_scl,
.data_reg = RTL9310_I2C_MST_DATA_CTRL,
.config_chan = rtl9300_i2c_config_chan,
.config_clock = rtl9300_i2c_config_clock,
.misc_init = rtl9300_i2c_init,
.rd_reg = RTL9310_I2C_MST_DATA_CTRL,
.wd_reg = RTL9310_I2C_MST_DATA_CTRL,
.max_nchan = RTL9310_I2C_MUX_NCHAN,
.max_data_len = RTL9300_I2C_MAX_DATA_LEN,
.reg_addr_8bit_len = RTL9300_REG_ADDR_8BIT_LEN,
};
static const struct rtl9300_i2c_drv_data rtl9607_i2c_drv_data = {
.field_desc = {
[F_SCL_SEL] = GLB_REG_FIELD(RTL9607_IO_MODE_EN, 13, 14),
[F_EXT_SCK_5MS] = MST_REG_FIELD(RTL9607_I2C_CONFIG, 26, 26),
[F_DEV_ADDR] = MST_REG_FIELD(RTL9607_I2C_CONFIG, 14, 20),
[F_MEM_ADDR_WIDTH] = MST_REG_FIELD(RTL9607_I2C_CONFIG, 12, 13),
[F_DATA_WIDTH] = MST_REG_FIELD(RTL9607_I2C_CONFIG, 10, 11),
[F_CLK_DIV] = MST_REG_FIELD(RTL9607_I2C_CONFIG, 0, 9),
[F_I2C_FAIL] = MST_REG_FIELD(RTL9607_I2C_IND_CMD, 3, 3),
[F_BUSY] = MST_REG_FIELD(RTL9607_I2C_IND_CMD, 2, 2),
[F_RWOP] = MST_REG_FIELD(RTL9607_I2C_IND_CMD, 1, 1),
[F_I2C_TRIG] = MST_REG_FIELD(RTL9607_I2C_IND_CMD, 0, 0),
[F_MEM_ADDR] = MST_REG_FIELD(RTL9607_I2C_IND_ADR, 0, 31),
},
.select_scl = rtl9310_i2c_select_scl,
.config_chan = rtl9607_i2c_config_chan,
.config_clock = rtl9607_i2c_config_clock,
.misc_init = rtl9607_i2c_init,
.rd_reg = RTL9607_I2C_IND_RD,
.wd_reg = RTL9607_I2C_IND_WD,
.max_nchan = RTL9607_I2C_MUX_NCHAN,
.max_data_len = RTL9607_I2C_MAX_DATA_LEN,
.reg_addr_8bit_len = RTL9607_REG_ADDR_8BIT_LEN,
};
static const struct of_device_id i2c_rtl9300_dt_ids[] = {
@@ -520,6 +652,7 @@ static const struct of_device_id i2c_rtl9300_dt_ids[] = {
{ .compatible = "realtek,rtl9311-i2c", .data = (void *) &rtl9310_i2c_drv_data },
{ .compatible = "realtek,rtl9312-i2c", .data = (void *) &rtl9310_i2c_drv_data },
{ .compatible = "realtek,rtl9313-i2c", .data = (void *) &rtl9310_i2c_drv_data },
{ .compatible = "realtek,rtl9607-i2c", .data = (void *) &rtl9607_i2c_drv_data },
{}
};
MODULE_DEVICE_TABLE(of, i2c_rtl9300_dt_ids);
+6 -1
View File
@@ -503,8 +503,13 @@ static void i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat)
i2c->msg->buf[i2c->msg_ptr++] = byte;
/* Add actual length to read for smbus block read */
if (i2c->msg->flags & I2C_M_RECV_LEN && i2c->msg->len == 1)
if (i2c->msg->flags & I2C_M_RECV_LEN && i2c->msg->len == 1) {
if (byte == 0 || byte > I2C_SMBUS_BLOCK_MAX) {
s3c24xx_i2c_stop(i2c, -EPROTO);
break;
}
i2c->msg->len += byte;
}
prepare_read:
if (is_msglast(i2c)) {
/* last byte of buffer */
+393 -137
View File
@@ -30,38 +30,29 @@
#define BYTES_PER_FIFO_WORD 4
#define I2C_CNFG 0x000
#define I2C_CNFG_DEBOUNCE_CNT GENMASK(14, 12)
#define I2C_CNFG_PACKET_MODE_EN BIT(10)
#define I2C_CNFG_NEW_MASTER_FSM BIT(11)
#define I2C_CNFG_MULTI_MASTER_MODE BIT(17)
#define I2C_STATUS 0x01c
#define I2C_SL_CNFG 0x020
#define I2C_SL_CNFG_NACK BIT(1)
#define I2C_SL_CNFG_NEWSL BIT(2)
#define I2C_SL_ADDR1 0x02c
#define I2C_SL_ADDR2 0x030
#define I2C_TLOW_SEXT 0x034
#define I2C_TX_FIFO 0x050
#define I2C_RX_FIFO 0x054
#define I2C_PACKET_TRANSFER_STATUS 0x058
#define I2C_FIFO_CONTROL 0x05c
#define I2C_FIFO_CONTROL_TX_FLUSH BIT(1)
#define I2C_FIFO_CONTROL_RX_FLUSH BIT(0)
#define I2C_FIFO_CONTROL_TX_TRIG(x) (((x) - 1) << 5)
#define I2C_FIFO_CONTROL_RX_TRIG(x) (((x) - 1) << 2)
#define I2C_FIFO_STATUS 0x060
#define I2C_FIFO_STATUS_TX GENMASK(7, 4)
#define I2C_FIFO_STATUS_RX GENMASK(3, 0)
#define I2C_INT_MASK 0x064
#define I2C_INT_STATUS 0x068
#define I2C_INT_BUS_CLR_DONE BIT(11)
#define I2C_INT_PACKET_XFER_COMPLETE BIT(7)
#define I2C_INT_NO_ACK BIT(3)
#define I2C_INT_ARBITRATION_LOST BIT(2)
#define I2C_INT_TX_FIFO_DATA_REQ BIT(1)
#define I2C_INT_RX_FIFO_DATA_REQ BIT(0)
#define I2C_CLK_DIVISOR 0x06c
#define I2C_CLK_DIVISOR_STD_FAST_MODE GENMASK(31, 16)
#define I2C_CLK_DIVISOR_HSMODE GENMASK(15, 0)
@@ -94,50 +85,38 @@
#define I2C_HEADER_CONTINUE_XFER BIT(15)
#define I2C_HEADER_SLAVE_ADDR_SHIFT 1
#define I2C_BUS_CLEAR_CNFG 0x084
#define I2C_BC_SCLK_THRESHOLD GENMASK(23, 16)
#define I2C_BC_STOP_COND BIT(2)
#define I2C_BC_TERMINATE BIT(1)
#define I2C_BC_ENABLE BIT(0)
#define I2C_BUS_CLEAR_STATUS 0x088
#define I2C_BC_STATUS BIT(0)
#define I2C_CONFIG_LOAD 0x08c
#define I2C_MSTR_CONFIG_LOAD BIT(0)
#define I2C_CLKEN_OVERRIDE 0x090
#define I2C_MST_CORE_CLKEN_OVR BIT(0)
#define I2C_INTERFACE_TIMING_0 0x094
#define I2C_INTERFACE_TIMING_THIGH GENMASK(13, 8)
#define I2C_INTERFACE_TIMING_TLOW GENMASK(5, 0)
#define I2C_INTERFACE_TIMING_1 0x098
#define I2C_INTERFACE_TIMING_TBUF GENMASK(29, 24)
#define I2C_INTERFACE_TIMING_TSU_STO GENMASK(21, 16)
#define I2C_INTERFACE_TIMING_THD_STA GENMASK(13, 8)
#define I2C_INTERFACE_TIMING_TSU_STA GENMASK(5, 0)
#define I2C_INTERFACE_TIMING_THIGH GENMASK(13, 8)
#define I2C_INTERFACE_TIMING_TLOW GENMASK(5, 0)
#define I2C_INTERFACE_TIMING_TBUF GENMASK(29, 24)
#define I2C_INTERFACE_TIMING_TSU_STO GENMASK(21, 16)
#define I2C_INTERFACE_TIMING_THD_STA GENMASK(13, 8)
#define I2C_INTERFACE_TIMING_TSU_STA GENMASK(5, 0)
#define I2C_HS_INTERFACE_TIMING_0 0x09c
#define I2C_HS_INTERFACE_TIMING_THIGH GENMASK(13, 8)
#define I2C_HS_INTERFACE_TIMING_TLOW GENMASK(5, 0)
#define I2C_HS_INTERFACE_TIMING_1 0x0a0
#define I2C_HS_INTERFACE_TIMING_TSU_STO GENMASK(21, 16)
#define I2C_HS_INTERFACE_TIMING_THD_STA GENMASK(13, 8)
#define I2C_HS_INTERFACE_TIMING_TSU_STA GENMASK(5, 0)
#define I2C_HS_INTERFACE_TIMING_THIGH GENMASK(13, 8)
#define I2C_HS_INTERFACE_TIMING_TLOW GENMASK(5, 0)
#define I2C_HS_INTERFACE_TIMING_TSU_STO GENMASK(21, 16)
#define I2C_HS_INTERFACE_TIMING_THD_STA GENMASK(13, 8)
#define I2C_HS_INTERFACE_TIMING_TSU_STA GENMASK(5, 0)
#define I2C_MST_FIFO_CONTROL 0x0b4
#define I2C_MST_FIFO_CONTROL_RX_FLUSH BIT(0)
#define I2C_MST_FIFO_CONTROL_TX_FLUSH BIT(1)
#define I2C_MST_FIFO_CONTROL_RX_TRIG(x) (((x) - 1) << 4)
#define I2C_MST_FIFO_CONTROL_TX_TRIG(x) (((x) - 1) << 16)
#define I2C_MST_FIFO_STATUS 0x0b8
#define I2C_MST_FIFO_STATUS_TX GENMASK(23, 16)
#define I2C_MST_FIFO_STATUS_RX GENMASK(7, 0)
#define I2C_MASTER_RESET_CNTRL 0x0a8
#define I2C_SW_MUTEX 0x0ec
#define I2C_SW_MUTEX_REQUEST GENMASK(3, 0)
#define I2C_SW_MUTEX_GRANT GENMASK(7, 4)
#define I2C_SW_MUTEX_ID_CCPLEX 9
@@ -159,6 +138,171 @@
*/
#define I2C_PIO_MODE_PREFERRED_LEN 32
struct tegra_i2c_regs {
unsigned int cnfg;
unsigned int status;
unsigned int sl_cnfg;
unsigned int sl_addr1;
unsigned int sl_addr2;
unsigned int tlow_sext;
unsigned int tx_fifo;
unsigned int rx_fifo;
unsigned int packet_transfer_status;
unsigned int fifo_control;
unsigned int fifo_status;
unsigned int int_mask;
unsigned int int_status;
unsigned int clk_divisor;
unsigned int bus_clear_cnfg;
unsigned int bus_clear_status;
unsigned int config_load;
unsigned int clken_override;
unsigned int interface_timing_0;
unsigned int interface_timing_1;
unsigned int hs_interface_timing_0;
unsigned int hs_interface_timing_1;
unsigned int master_reset_cntrl;
unsigned int mst_fifo_control;
unsigned int mst_fifo_status;
unsigned int sw_mutex;
};
static const struct tegra_i2c_regs tegra20_i2c_regs = {
.cnfg = 0x000,
.status = 0x01c,
.sl_cnfg = 0x020,
.sl_addr1 = 0x02c,
.sl_addr2 = 0x030,
.tx_fifo = 0x050,
.rx_fifo = 0x054,
.packet_transfer_status = 0x058,
.fifo_control = 0x05c,
.fifo_status = 0x060,
.int_mask = 0x064,
.int_status = 0x068,
.clk_divisor = 0x06c,
.bus_clear_cnfg = 0x084,
.bus_clear_status = 0x088,
.config_load = 0x08c,
.clken_override = 0x090,
.interface_timing_0 = 0x094,
.interface_timing_1 = 0x098,
.hs_interface_timing_0 = 0x09c,
.hs_interface_timing_1 = 0x0a0,
.master_reset_cntrl = 0x0a8,
.mst_fifo_control = 0x0b4,
.mst_fifo_status = 0x0b8,
};
#if IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC)
static const struct tegra_i2c_regs tegra20_dvc_i2c_regs = {
.cnfg = 0x040,
.status = 0x05c,
.tx_fifo = 0x060,
.rx_fifo = 0x064,
.packet_transfer_status = 0x068,
.fifo_control = 0x06c,
.fifo_status = 0x070,
.int_mask = 0x074,
.int_status = 0x078,
.clk_divisor = 0x07c,
.bus_clear_cnfg = 0x094,
.bus_clear_status = 0x098,
.config_load = 0x09c,
.clken_override = 0x0a0,
.interface_timing_0 = 0x0a4,
.interface_timing_1 = 0x0a8,
.hs_interface_timing_0 = 0x0ac,
.hs_interface_timing_1 = 0x0b0,
.master_reset_cntrl = 0x0b8,
.mst_fifo_control = 0x0c4,
.mst_fifo_status = 0x0c8,
};
#endif
#if IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC)
static const struct tegra_i2c_regs tegra210_vi_i2c_regs = {
.cnfg = 0x0c00,
.status = 0x0c70,
.tlow_sext = 0x0cd0,
.tx_fifo = 0x0d40,
.rx_fifo = 0x0d50,
.packet_transfer_status = 0x0d60,
.fifo_control = 0x0d70,
.fifo_status = 0x0d80,
.int_mask = 0x0d90,
.int_status = 0x0da0,
.clk_divisor = 0x0db0,
.bus_clear_cnfg = 0x0e10,
.bus_clear_status = 0x0e20,
.config_load = 0x0e30,
.clken_override = 0x0e40,
.interface_timing_0 = 0x0e50,
.interface_timing_1 = 0x0e60,
.hs_interface_timing_0 = 0x0e70,
.hs_interface_timing_1 = 0x0e80,
.master_reset_cntrl = 0x0ea0,
.mst_fifo_control = 0x0ed0,
.mst_fifo_status = 0x0ee0,
};
#endif
static const struct tegra_i2c_regs tegra264_i2c_regs = {
.cnfg = 0x000,
.status = 0x01c,
.sl_cnfg = 0x020,
.sl_addr1 = 0x02c,
.sl_addr2 = 0x030,
.tx_fifo = 0x050,
.rx_fifo = 0x054,
.packet_transfer_status = 0x058,
.fifo_control = 0x05c,
.fifo_status = 0x060,
.int_mask = 0x064,
.int_status = 0x068,
.clk_divisor = 0x06c,
.bus_clear_cnfg = 0x084,
.bus_clear_status = 0x088,
.config_load = 0x08c,
.clken_override = 0x090,
.interface_timing_0 = 0x094,
.interface_timing_1 = 0x098,
.hs_interface_timing_0 = 0x09c,
.hs_interface_timing_1 = 0x0a0,
.master_reset_cntrl = 0x0a8,
.mst_fifo_control = 0x0b4,
.mst_fifo_status = 0x0b8,
.sw_mutex = 0x0ec,
};
static const struct tegra_i2c_regs tegra410_i2c_regs = {
.cnfg = 0x000,
.status = 0x01c,
.sl_cnfg = 0x020,
.sl_addr1 = 0x02c,
.sl_addr2 = 0x030,
.tx_fifo = 0x054,
.rx_fifo = 0x058,
.packet_transfer_status = 0x05c,
.fifo_control = 0x060,
.fifo_status = 0x064,
.int_mask = 0x068,
.int_status = 0x06c,
.clk_divisor = 0x070,
.bus_clear_cnfg = 0x088,
.bus_clear_status = 0x08c,
.config_load = 0x090,
.clken_override = 0x094,
.interface_timing_0 = 0x098,
.interface_timing_1 = 0x09c,
.hs_interface_timing_0 = 0x0a0,
.hs_interface_timing_1 = 0x0a4,
.master_reset_cntrl = 0x0ac,
.mst_fifo_control = 0x0b8,
.mst_fifo_status = 0x0bc,
.sw_mutex = 0x0f0,
};
/*
* msg_end_type: The bus control which needs to be sent at end of transfer.
* @MSG_END_STOP: Send stop pulse.
@@ -171,6 +315,18 @@ enum msg_end_type {
MSG_END_CONTINUE,
};
/*
* tegra_i2c_variant: Identifies the variant of I2C controller.
* @TEGRA_I2C_VARIANT_DEFAULT: Identifies the default I2C controller.
* @TEGRA_I2C_VARIANT_DVC: Identifies the DVC I2C controller, has a different register layout.
* @TEGRA_I2C_VARIANT_VI: Identifies the VI I2C controller, has a different register layout.
*/
enum tegra_i2c_variant {
TEGRA_I2C_VARIANT_DEFAULT,
TEGRA_I2C_VARIANT_DVC,
TEGRA_I2C_VARIANT_VI,
};
/**
* struct tegra_i2c_hw_feature : per hardware generation features
* @has_continue_xfer_support: continue-transfer supported
@@ -223,6 +379,8 @@ enum msg_end_type {
* timing settings.
* @enable_hs_mode_support: Enable support for high speed (HS) mode transfers.
* @has_mutex: Has mutex register for mutual exclusion with other firmwares or VMs.
* @variant: This represents the I2C controller variant.
* @regs: Register offsets for the specific SoC variant.
*/
struct tegra_i2c_hw_feature {
bool has_continue_xfer_support;
@@ -254,6 +412,8 @@ struct tegra_i2c_hw_feature {
bool has_interface_timing_reg;
bool enable_hs_mode_support;
bool has_mutex;
enum tegra_i2c_variant variant;
const struct tegra_i2c_regs *regs;
};
/**
@@ -268,8 +428,6 @@ struct tegra_i2c_hw_feature {
* @base_phys: physical base address of the I2C controller
* @cont_id: I2C controller ID, used for packet header
* @irq: IRQ number of transfer complete interrupt
* @is_dvc: identifies the DVC I2C controller, has a different register layout
* @is_vi: identifies the VI I2C controller, has a different register layout
* @msg_complete: transfer completion notifier
* @msg_buf_remaining: size of unsent data in the message buffer
* @msg_len: length of message in current transfer
@@ -321,12 +479,12 @@ struct tegra_i2c_dev {
bool atomic_mode;
bool dma_mode;
bool msg_read;
bool is_dvc;
bool is_vi;
};
#define IS_DVC(dev) (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC) && (dev)->is_dvc)
#define IS_VI(dev) (IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC) && (dev)->is_vi)
#define IS_DVC(dev) (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC) && \
(dev)->hw->variant == TEGRA_I2C_VARIANT_DVC)
#define IS_VI(dev) (IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC) && \
(dev)->hw->variant == TEGRA_I2C_VARIANT_VI)
static void dvc_writel(struct tegra_i2c_dev *i2c_dev, u32 val,
unsigned int reg)
@@ -339,40 +497,26 @@ static u32 dvc_readl(struct tegra_i2c_dev *i2c_dev, unsigned int reg)
return readl_relaxed(i2c_dev->base + reg);
}
/*
* If necessary, i2c_writel() and i2c_readl() will offset the register
* in order to talk to the I2C block inside the DVC block.
*/
static u32 tegra_i2c_reg_addr(struct tegra_i2c_dev *i2c_dev, unsigned int reg)
{
if (IS_DVC(i2c_dev))
reg += (reg >= I2C_TX_FIFO) ? 0x10 : 0x40;
else if (IS_VI(i2c_dev))
reg = 0xc00 + (reg << 2);
return reg;
}
static void i2c_writel(struct tegra_i2c_dev *i2c_dev, u32 val, unsigned int reg)
{
writel_relaxed(val, i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg));
writel_relaxed(val, i2c_dev->base + reg);
/* read back register to make sure that register writes completed */
if (reg != I2C_TX_FIFO)
readl_relaxed(i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg));
if (reg != i2c_dev->hw->regs->tx_fifo)
readl_relaxed(i2c_dev->base + reg);
else if (IS_VI(i2c_dev))
readl_relaxed(i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, I2C_INT_STATUS));
readl_relaxed(i2c_dev->base + i2c_dev->hw->regs->int_status);
}
static u32 i2c_readl(struct tegra_i2c_dev *i2c_dev, unsigned int reg)
{
return readl_relaxed(i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg));
return readl_relaxed(i2c_dev->base + reg);
}
static void i2c_writesl(struct tegra_i2c_dev *i2c_dev, void *data,
unsigned int reg, unsigned int len)
{
writesl(i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg), data, len);
writesl(i2c_dev->base + reg, data, len);
}
static void i2c_writesl_vi(struct tegra_i2c_dev *i2c_dev, void *data,
@@ -393,12 +537,12 @@ static void i2c_writesl_vi(struct tegra_i2c_dev *i2c_dev, void *data,
static void i2c_readsl(struct tegra_i2c_dev *i2c_dev, void *data,
unsigned int reg, unsigned int len)
{
readsl(i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg), data, len);
readsl(i2c_dev->base + reg, data, len);
}
static bool tegra_i2c_mutex_acquired(struct tegra_i2c_dev *i2c_dev)
{
unsigned int reg = tegra_i2c_reg_addr(i2c_dev, I2C_SW_MUTEX);
unsigned int reg = i2c_dev->hw->regs->sw_mutex;
u32 val, id;
val = readl(i2c_dev->base + reg);
@@ -409,7 +553,7 @@ static bool tegra_i2c_mutex_acquired(struct tegra_i2c_dev *i2c_dev)
static bool tegra_i2c_mutex_trylock(struct tegra_i2c_dev *i2c_dev)
{
unsigned int reg = tegra_i2c_reg_addr(i2c_dev, I2C_SW_MUTEX);
unsigned int reg = i2c_dev->hw->regs->sw_mutex;
u32 val, id;
val = readl(i2c_dev->base + reg);
@@ -447,7 +591,7 @@ static int tegra_i2c_mutex_lock(struct tegra_i2c_dev *i2c_dev)
static int tegra_i2c_mutex_unlock(struct tegra_i2c_dev *i2c_dev)
{
unsigned int reg = tegra_i2c_reg_addr(i2c_dev, I2C_SW_MUTEX);
unsigned int reg = i2c_dev->hw->regs->sw_mutex;
u32 val, id;
if (!i2c_dev->hw->has_mutex)
@@ -470,16 +614,16 @@ static void tegra_i2c_mask_irq(struct tegra_i2c_dev *i2c_dev, u32 mask)
{
u32 int_mask;
int_mask = i2c_readl(i2c_dev, I2C_INT_MASK) & ~mask;
i2c_writel(i2c_dev, int_mask, I2C_INT_MASK);
int_mask = i2c_readl(i2c_dev, i2c_dev->hw->regs->int_mask) & ~mask;
i2c_writel(i2c_dev, int_mask, i2c_dev->hw->regs->int_mask);
}
static void tegra_i2c_unmask_irq(struct tegra_i2c_dev *i2c_dev, u32 mask)
{
u32 int_mask;
int_mask = i2c_readl(i2c_dev, I2C_INT_MASK) | mask;
i2c_writel(i2c_dev, int_mask, I2C_INT_MASK);
int_mask = i2c_readl(i2c_dev, i2c_dev->hw->regs->int_mask) | mask;
i2c_writel(i2c_dev, int_mask, i2c_dev->hw->regs->int_mask);
}
static void tegra_i2c_dma_complete(void *args)
@@ -623,34 +767,34 @@ static void tegra_i2c_vi_init(struct tegra_i2c_dev *i2c_dev)
value = FIELD_PREP(I2C_INTERFACE_TIMING_THIGH, 2) |
FIELD_PREP(I2C_INTERFACE_TIMING_TLOW, 4);
i2c_writel(i2c_dev, value, I2C_INTERFACE_TIMING_0);
i2c_writel(i2c_dev, value, i2c_dev->hw->regs->interface_timing_0);
value = FIELD_PREP(I2C_INTERFACE_TIMING_TBUF, 4) |
FIELD_PREP(I2C_INTERFACE_TIMING_TSU_STO, 7) |
FIELD_PREP(I2C_INTERFACE_TIMING_THD_STA, 4) |
FIELD_PREP(I2C_INTERFACE_TIMING_TSU_STA, 4);
i2c_writel(i2c_dev, value, I2C_INTERFACE_TIMING_1);
i2c_writel(i2c_dev, value, i2c_dev->hw->regs->interface_timing_1);
value = FIELD_PREP(I2C_HS_INTERFACE_TIMING_THIGH, 3) |
FIELD_PREP(I2C_HS_INTERFACE_TIMING_TLOW, 8);
i2c_writel(i2c_dev, value, I2C_HS_INTERFACE_TIMING_0);
i2c_writel(i2c_dev, value, i2c_dev->hw->regs->hs_interface_timing_0);
value = FIELD_PREP(I2C_HS_INTERFACE_TIMING_TSU_STO, 11) |
FIELD_PREP(I2C_HS_INTERFACE_TIMING_THD_STA, 11) |
FIELD_PREP(I2C_HS_INTERFACE_TIMING_TSU_STA, 11);
i2c_writel(i2c_dev, value, I2C_HS_INTERFACE_TIMING_1);
i2c_writel(i2c_dev, value, i2c_dev->hw->regs->hs_interface_timing_1);
value = FIELD_PREP(I2C_BC_SCLK_THRESHOLD, 9) | I2C_BC_STOP_COND;
i2c_writel(i2c_dev, value, I2C_BUS_CLEAR_CNFG);
i2c_writel(i2c_dev, value, i2c_dev->hw->regs->bus_clear_cnfg);
i2c_writel(i2c_dev, 0x0, I2C_TLOW_SEXT);
i2c_writel(i2c_dev, 0x0, i2c_dev->hw->regs->tlow_sext);
}
static int tegra_i2c_poll_register(struct tegra_i2c_dev *i2c_dev,
u32 reg, u32 mask, u32 delay_us,
u32 timeout_us)
{
void __iomem *addr = i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg);
void __iomem *addr = i2c_dev->base + reg;
u32 val;
if (!i2c_dev->atomic_mode)
@@ -669,11 +813,11 @@ static int tegra_i2c_flush_fifos(struct tegra_i2c_dev *i2c_dev)
if (i2c_dev->hw->has_mst_fifo) {
mask = I2C_MST_FIFO_CONTROL_TX_FLUSH |
I2C_MST_FIFO_CONTROL_RX_FLUSH;
offset = I2C_MST_FIFO_CONTROL;
offset = i2c_dev->hw->regs->mst_fifo_control;
} else {
mask = I2C_FIFO_CONTROL_TX_FLUSH |
I2C_FIFO_CONTROL_RX_FLUSH;
offset = I2C_FIFO_CONTROL;
offset = i2c_dev->hw->regs->fifo_control;
}
val = i2c_readl(i2c_dev, offset);
@@ -696,9 +840,9 @@ static int tegra_i2c_wait_for_config_load(struct tegra_i2c_dev *i2c_dev)
if (!i2c_dev->hw->has_config_load_reg)
return 0;
i2c_writel(i2c_dev, I2C_MSTR_CONFIG_LOAD, I2C_CONFIG_LOAD);
i2c_writel(i2c_dev, I2C_MSTR_CONFIG_LOAD, i2c_dev->hw->regs->config_load);
err = tegra_i2c_poll_register(i2c_dev, I2C_CONFIG_LOAD, 0xffffffff,
err = tegra_i2c_poll_register(i2c_dev, i2c_dev->hw->regs->config_load, 0xffffffff,
1000, I2C_CONFIG_LOAD_TIMEOUT);
if (err) {
dev_err(i2c_dev->dev, "failed to load config\n");
@@ -719,10 +863,10 @@ static int tegra_i2c_master_reset(struct tegra_i2c_dev *i2c_dev)
* SW needs to wait for 2us after assertion and de-assertion of this soft
* reset.
*/
i2c_writel(i2c_dev, 0x1, I2C_MASTER_RESET_CNTRL);
i2c_writel(i2c_dev, 0x1, i2c_dev->hw->regs->master_reset_cntrl);
fsleep(2);
i2c_writel(i2c_dev, 0x0, I2C_MASTER_RESET_CNTRL);
i2c_writel(i2c_dev, 0x0, i2c_dev->hw->regs->master_reset_cntrl);
fsleep(2);
return 0;
@@ -764,8 +908,8 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
if (i2c_dev->hw->has_multi_master_mode)
val |= I2C_CNFG_MULTI_MASTER_MODE;
i2c_writel(i2c_dev, val, I2C_CNFG);
i2c_writel(i2c_dev, 0, I2C_INT_MASK);
i2c_writel(i2c_dev, val, i2c_dev->hw->regs->cnfg);
i2c_writel(i2c_dev, 0, i2c_dev->hw->regs->int_mask);
if (IS_VI(i2c_dev))
tegra_i2c_vi_init(i2c_dev);
@@ -810,12 +954,12 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
clk_divisor = FIELD_PREP(I2C_CLK_DIVISOR_HSMODE,
i2c_dev->hw->clk_divisor_hs_mode) |
FIELD_PREP(I2C_CLK_DIVISOR_STD_FAST_MODE, non_hs_mode);
i2c_writel(i2c_dev, clk_divisor, I2C_CLK_DIVISOR);
i2c_writel(i2c_dev, clk_divisor, i2c_dev->hw->regs->clk_divisor);
if (i2c_dev->hw->has_interface_timing_reg) {
val = FIELD_PREP(I2C_INTERFACE_TIMING_THIGH, thigh) |
FIELD_PREP(I2C_INTERFACE_TIMING_TLOW, tlow);
i2c_writel(i2c_dev, val, I2C_INTERFACE_TIMING_0);
i2c_writel(i2c_dev, val, i2c_dev->hw->regs->interface_timing_0);
}
/*
@@ -823,7 +967,7 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
* Otherwise, preserve the chip default values.
*/
if (i2c_dev->hw->has_interface_timing_reg && tsu_thd)
i2c_writel(i2c_dev, tsu_thd, I2C_INTERFACE_TIMING_1);
i2c_writel(i2c_dev, tsu_thd, i2c_dev->hw->regs->interface_timing_1);
/* Write HS mode registers. These will get used only for HS mode*/
if (i2c_dev->hw->enable_hs_mode_support) {
@@ -833,8 +977,8 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
val = FIELD_PREP(I2C_HS_INTERFACE_TIMING_THIGH, thigh) |
FIELD_PREP(I2C_HS_INTERFACE_TIMING_TLOW, tlow);
i2c_writel(i2c_dev, val, I2C_HS_INTERFACE_TIMING_0);
i2c_writel(i2c_dev, tsu_thd, I2C_HS_INTERFACE_TIMING_1);
i2c_writel(i2c_dev, val, i2c_dev->hw->regs->hs_interface_timing_0);
i2c_writel(i2c_dev, tsu_thd, i2c_dev->hw->regs->hs_interface_timing_1);
}
clk_multiplier = (tlow + thigh + 2) * (non_hs_mode + 1);
@@ -847,12 +991,12 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
}
if (!IS_DVC(i2c_dev) && !IS_VI(i2c_dev)) {
u32 sl_cfg = i2c_readl(i2c_dev, I2C_SL_CNFG);
u32 sl_cfg = i2c_readl(i2c_dev, i2c_dev->hw->regs->sl_cnfg);
sl_cfg |= I2C_SL_CNFG_NACK | I2C_SL_CNFG_NEWSL;
i2c_writel(i2c_dev, sl_cfg, I2C_SL_CNFG);
i2c_writel(i2c_dev, 0xfc, I2C_SL_ADDR1);
i2c_writel(i2c_dev, 0x00, I2C_SL_ADDR2);
i2c_writel(i2c_dev, sl_cfg, i2c_dev->hw->regs->sl_cnfg);
i2c_writel(i2c_dev, 0xfc, i2c_dev->hw->regs->sl_addr1);
i2c_writel(i2c_dev, 0x00, i2c_dev->hw->regs->sl_addr2);
}
err = tegra_i2c_flush_fifos(i2c_dev);
@@ -860,7 +1004,7 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
return err;
if (i2c_dev->multimaster_mode && i2c_dev->hw->has_slcg_override_reg)
i2c_writel(i2c_dev, I2C_MST_CORE_CLKEN_OVR, I2C_CLKEN_OVERRIDE);
i2c_writel(i2c_dev, I2C_MST_CORE_CLKEN_OVR, i2c_dev->hw->regs->clken_override);
err = tegra_i2c_wait_for_config_load(i2c_dev);
if (err)
@@ -881,9 +1025,9 @@ static int tegra_i2c_disable_packet_mode(struct tegra_i2c_dev *i2c_dev)
*/
udelay(DIV_ROUND_UP(2 * 1000000, i2c_dev->timings.bus_freq_hz));
cnfg = i2c_readl(i2c_dev, I2C_CNFG);
cnfg = i2c_readl(i2c_dev, i2c_dev->hw->regs->cnfg);
if (cnfg & I2C_CNFG_PACKET_MODE_EN)
i2c_writel(i2c_dev, cnfg & ~I2C_CNFG_PACKET_MODE_EN, I2C_CNFG);
i2c_writel(i2c_dev, cnfg & ~I2C_CNFG_PACKET_MODE_EN, i2c_dev->hw->regs->cnfg);
return tegra_i2c_wait_for_config_load(i2c_dev);
}
@@ -903,10 +1047,10 @@ static int tegra_i2c_empty_rx_fifo(struct tegra_i2c_dev *i2c_dev)
return -EINVAL;
if (i2c_dev->hw->has_mst_fifo) {
val = i2c_readl(i2c_dev, I2C_MST_FIFO_STATUS);
val = i2c_readl(i2c_dev, i2c_dev->hw->regs->mst_fifo_status);
rx_fifo_avail = FIELD_GET(I2C_MST_FIFO_STATUS_RX, val);
} else {
val = i2c_readl(i2c_dev, I2C_FIFO_STATUS);
val = i2c_readl(i2c_dev, i2c_dev->hw->regs->fifo_status);
rx_fifo_avail = FIELD_GET(I2C_FIFO_STATUS_RX, val);
}
@@ -915,7 +1059,7 @@ static int tegra_i2c_empty_rx_fifo(struct tegra_i2c_dev *i2c_dev)
if (words_to_transfer > rx_fifo_avail)
words_to_transfer = rx_fifo_avail;
i2c_readsl(i2c_dev, buf, I2C_RX_FIFO, words_to_transfer);
i2c_readsl(i2c_dev, buf, i2c_dev->hw->regs->rx_fifo, words_to_transfer);
buf += words_to_transfer * BYTES_PER_FIFO_WORD;
buf_remaining -= words_to_transfer * BYTES_PER_FIFO_WORD;
@@ -931,7 +1075,7 @@ static int tegra_i2c_empty_rx_fifo(struct tegra_i2c_dev *i2c_dev)
* when (words_to_transfer was > rx_fifo_avail) earlier
* in this function.
*/
val = i2c_readl(i2c_dev, I2C_RX_FIFO);
val = i2c_readl(i2c_dev, i2c_dev->hw->regs->rx_fifo);
val = cpu_to_le32(val);
memcpy(buf, &val, buf_remaining);
buf_remaining = 0;
@@ -956,10 +1100,10 @@ static int tegra_i2c_fill_tx_fifo(struct tegra_i2c_dev *i2c_dev)
u32 val;
if (i2c_dev->hw->has_mst_fifo) {
val = i2c_readl(i2c_dev, I2C_MST_FIFO_STATUS);
val = i2c_readl(i2c_dev, i2c_dev->hw->regs->mst_fifo_status);
tx_fifo_avail = FIELD_GET(I2C_MST_FIFO_STATUS_TX, val);
} else {
val = i2c_readl(i2c_dev, I2C_FIFO_STATUS);
val = i2c_readl(i2c_dev, i2c_dev->hw->regs->fifo_status);
tx_fifo_avail = FIELD_GET(I2C_FIFO_STATUS_TX, val);
}
@@ -990,9 +1134,9 @@ static int tegra_i2c_fill_tx_fifo(struct tegra_i2c_dev *i2c_dev)
i2c_dev->msg_buf = buf + words_to_transfer * BYTES_PER_FIFO_WORD;
if (IS_VI(i2c_dev))
i2c_writesl_vi(i2c_dev, buf, I2C_TX_FIFO, words_to_transfer);
i2c_writesl_vi(i2c_dev, buf, i2c_dev->hw->regs->tx_fifo, words_to_transfer);
else
i2c_writesl(i2c_dev, buf, I2C_TX_FIFO, words_to_transfer);
i2c_writesl(i2c_dev, buf, i2c_dev->hw->regs->tx_fifo, words_to_transfer);
buf += words_to_transfer * BYTES_PER_FIFO_WORD;
}
@@ -1014,7 +1158,7 @@ static int tegra_i2c_fill_tx_fifo(struct tegra_i2c_dev *i2c_dev)
i2c_dev->msg_buf_remaining = 0;
i2c_dev->msg_buf = NULL;
i2c_writel(i2c_dev, val, I2C_TX_FIFO);
i2c_writel(i2c_dev, val, i2c_dev->hw->regs->tx_fifo);
}
return 0;
@@ -1026,13 +1170,13 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
struct tegra_i2c_dev *i2c_dev = dev_id;
u32 status;
status = i2c_readl(i2c_dev, I2C_INT_STATUS);
status = i2c_readl(i2c_dev, i2c_dev->hw->regs->int_status);
if (status == 0) {
dev_warn(i2c_dev->dev, "IRQ status 0 %08x %08x %08x\n",
i2c_readl(i2c_dev, I2C_PACKET_TRANSFER_STATUS),
i2c_readl(i2c_dev, I2C_STATUS),
i2c_readl(i2c_dev, I2C_CNFG));
i2c_readl(i2c_dev, i2c_dev->hw->regs->packet_transfer_status),
i2c_readl(i2c_dev, i2c_dev->hw->regs->status),
i2c_readl(i2c_dev, i2c_dev->hw->regs->cnfg));
i2c_dev->msg_err |= I2C_ERR_UNKNOWN_INTERRUPT;
goto err;
}
@@ -1075,7 +1219,7 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
}
}
i2c_writel(i2c_dev, status, I2C_INT_STATUS);
i2c_writel(i2c_dev, status, i2c_dev->hw->regs->int_status);
if (IS_DVC(i2c_dev))
dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS);
@@ -1113,7 +1257,7 @@ err:
if (i2c_dev->hw->supports_bus_clear)
tegra_i2c_mask_irq(i2c_dev, I2C_INT_BUS_CLR_DONE);
i2c_writel(i2c_dev, status, I2C_INT_STATUS);
i2c_writel(i2c_dev, status, i2c_dev->hw->regs->int_status);
if (IS_DVC(i2c_dev))
dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS);
@@ -1136,9 +1280,9 @@ static void tegra_i2c_config_fifo_trig(struct tegra_i2c_dev *i2c_dev,
int err;
if (i2c_dev->hw->has_mst_fifo)
reg = I2C_MST_FIFO_CONTROL;
reg = i2c_dev->hw->regs->mst_fifo_control;
else
reg = I2C_FIFO_CONTROL;
reg = i2c_dev->hw->regs->fifo_control;
if (i2c_dev->dma_mode) {
if (len & 0xF)
@@ -1149,7 +1293,7 @@ static void tegra_i2c_config_fifo_trig(struct tegra_i2c_dev *i2c_dev,
dma_burst = 8;
if (i2c_dev->msg_read) {
reg_offset = tegra_i2c_reg_addr(i2c_dev, I2C_RX_FIFO);
reg_offset = i2c_dev->hw->regs->rx_fifo;
slv_config.src_addr = i2c_dev->base_phys + reg_offset;
slv_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
@@ -1160,7 +1304,7 @@ static void tegra_i2c_config_fifo_trig(struct tegra_i2c_dev *i2c_dev,
else
val = I2C_FIFO_CONTROL_RX_TRIG(dma_burst);
} else {
reg_offset = tegra_i2c_reg_addr(i2c_dev, I2C_TX_FIFO);
reg_offset = i2c_dev->hw->regs->tx_fifo;
slv_config.dst_addr = i2c_dev->base_phys + reg_offset;
slv_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
@@ -1203,7 +1347,7 @@ static unsigned long tegra_i2c_poll_completion(struct tegra_i2c_dev *i2c_dev,
ktime_t ktimeout = ktime_add_ms(ktime, timeout_ms);
do {
u32 status = i2c_readl(i2c_dev, I2C_INT_STATUS);
u32 status = i2c_readl(i2c_dev, i2c_dev->hw->regs->int_status);
if (status)
tegra_i2c_isr(i2c_dev->irq, i2c_dev);
@@ -1262,14 +1406,14 @@ static int tegra_i2c_issue_bus_clear(struct i2c_adapter *adap)
val = FIELD_PREP(I2C_BC_SCLK_THRESHOLD, 9) | I2C_BC_STOP_COND |
I2C_BC_TERMINATE;
i2c_writel(i2c_dev, val, I2C_BUS_CLEAR_CNFG);
i2c_writel(i2c_dev, val, i2c_dev->hw->regs->bus_clear_cnfg);
err = tegra_i2c_wait_for_config_load(i2c_dev);
if (err)
return err;
val |= I2C_BC_ENABLE;
i2c_writel(i2c_dev, val, I2C_BUS_CLEAR_CNFG);
i2c_writel(i2c_dev, val, i2c_dev->hw->regs->bus_clear_cnfg);
tegra_i2c_unmask_irq(i2c_dev, I2C_INT_BUS_CLR_DONE);
time_left = tegra_i2c_wait_completion(i2c_dev, &i2c_dev->msg_complete, 50);
@@ -1280,7 +1424,7 @@ static int tegra_i2c_issue_bus_clear(struct i2c_adapter *adap)
return -ETIMEDOUT;
}
val = i2c_readl(i2c_dev, I2C_BUS_CLEAR_STATUS);
val = i2c_readl(i2c_dev, i2c_dev->hw->regs->bus_clear_status);
if (!(val & I2C_BC_STATUS)) {
dev_err(i2c_dev->dev, "un-recovered arbitration lost\n");
return -EIO;
@@ -1305,14 +1449,14 @@ static void tegra_i2c_push_packet_header(struct tegra_i2c_dev *i2c_dev,
if (i2c_dev->dma_mode && !i2c_dev->msg_read)
*dma_buf++ = packet_header;
else
i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
i2c_writel(i2c_dev, packet_header, i2c_dev->hw->regs->tx_fifo);
packet_header = i2c_dev->msg_len - 1;
if (i2c_dev->dma_mode && !i2c_dev->msg_read)
*dma_buf++ = packet_header;
else
i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
i2c_writel(i2c_dev, packet_header, i2c_dev->hw->regs->tx_fifo);
packet_header = I2C_HEADER_IE_ENABLE;
@@ -1340,7 +1484,7 @@ static void tegra_i2c_push_packet_header(struct tegra_i2c_dev *i2c_dev,
if (i2c_dev->dma_mode && !i2c_dev->msg_read)
*dma_buf++ = packet_header;
else
i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
i2c_writel(i2c_dev, packet_header, i2c_dev->hw->regs->tx_fifo);
}
static int tegra_i2c_error_recover(struct tegra_i2c_dev *i2c_dev,
@@ -1461,7 +1605,7 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
tegra_i2c_unmask_irq(i2c_dev, int_mask);
dev_dbg(i2c_dev->dev, "unmasked IRQ: %02x\n",
i2c_readl(i2c_dev, I2C_INT_MASK));
i2c_readl(i2c_dev, i2c_dev->hw->regs->int_mask));
if (i2c_dev->dma_mode) {
time_left = tegra_i2c_wait_completion(i2c_dev,
@@ -1635,8 +1779,44 @@ static const struct tegra_i2c_hw_feature tegra20_i2c_hw = {
.has_interface_timing_reg = false,
.enable_hs_mode_support = false,
.has_mutex = false,
.variant = TEGRA_I2C_VARIANT_DEFAULT,
.regs = &tegra20_i2c_regs,
};
#if IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC)
static const struct tegra_i2c_hw_feature tegra20_dvc_i2c_hw = {
.has_continue_xfer_support = false,
.has_per_pkt_xfer_complete_irq = false,
.clk_divisor_hs_mode = 3,
.clk_divisor_std_mode = 0,
.clk_divisor_fast_mode = 0,
.clk_divisor_fast_plus_mode = 0,
.has_config_load_reg = false,
.has_multi_master_mode = false,
.has_slcg_override_reg = false,
.has_mst_fifo = false,
.has_mst_reset = false,
.quirks = &tegra_i2c_quirks,
.supports_bus_clear = false,
.has_apb_dma = true,
.tlow_std_mode = 0x4,
.thigh_std_mode = 0x2,
.tlow_fast_mode = 0x4,
.thigh_fast_mode = 0x2,
.tlow_fastplus_mode = 0x4,
.thigh_fastplus_mode = 0x2,
.setup_hold_time_std_mode = 0x0,
.setup_hold_time_fast_mode = 0x0,
.setup_hold_time_fastplus_mode = 0x0,
.setup_hold_time_hs_mode = 0x0,
.has_interface_timing_reg = false,
.enable_hs_mode_support = false,
.has_mutex = false,
.variant = TEGRA_I2C_VARIANT_DVC,
.regs = &tegra20_dvc_i2c_regs,
};
#endif
static const struct tegra_i2c_hw_feature tegra30_i2c_hw = {
.has_continue_xfer_support = true,
.has_per_pkt_xfer_complete_irq = false,
@@ -1665,6 +1845,8 @@ static const struct tegra_i2c_hw_feature tegra30_i2c_hw = {
.has_interface_timing_reg = false,
.enable_hs_mode_support = false,
.has_mutex = false,
.variant = TEGRA_I2C_VARIANT_DEFAULT,
.regs = &tegra20_i2c_regs,
};
static const struct tegra_i2c_hw_feature tegra114_i2c_hw = {
@@ -1695,6 +1877,8 @@ static const struct tegra_i2c_hw_feature tegra114_i2c_hw = {
.has_interface_timing_reg = false,
.enable_hs_mode_support = false,
.has_mutex = false,
.variant = TEGRA_I2C_VARIANT_DEFAULT,
.regs = &tegra20_i2c_regs,
};
static const struct tegra_i2c_hw_feature tegra124_i2c_hw = {
@@ -1725,6 +1909,8 @@ static const struct tegra_i2c_hw_feature tegra124_i2c_hw = {
.has_interface_timing_reg = true,
.enable_hs_mode_support = false,
.has_mutex = false,
.variant = TEGRA_I2C_VARIANT_DEFAULT,
.regs = &tegra20_i2c_regs,
};
static const struct tegra_i2c_hw_feature tegra210_i2c_hw = {
@@ -1755,8 +1941,44 @@ static const struct tegra_i2c_hw_feature tegra210_i2c_hw = {
.has_interface_timing_reg = true,
.enable_hs_mode_support = false,
.has_mutex = false,
.variant = TEGRA_I2C_VARIANT_DEFAULT,
.regs = &tegra20_i2c_regs,
};
#if IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC)
static const struct tegra_i2c_hw_feature tegra210_vi_i2c_hw = {
.has_continue_xfer_support = true,
.has_per_pkt_xfer_complete_irq = true,
.clk_divisor_hs_mode = 1,
.clk_divisor_std_mode = 0x19,
.clk_divisor_fast_mode = 0x19,
.clk_divisor_fast_plus_mode = 0x10,
.has_config_load_reg = true,
.has_multi_master_mode = false,
.has_slcg_override_reg = true,
.has_mst_fifo = false,
.has_mst_reset = false,
.quirks = &tegra_i2c_quirks,
.supports_bus_clear = true,
.has_apb_dma = true,
.tlow_std_mode = 0x4,
.thigh_std_mode = 0x2,
.tlow_fast_mode = 0x4,
.thigh_fast_mode = 0x2,
.tlow_fastplus_mode = 0x4,
.thigh_fastplus_mode = 0x2,
.setup_hold_time_std_mode = 0,
.setup_hold_time_fast_mode = 0,
.setup_hold_time_fastplus_mode = 0,
.setup_hold_time_hs_mode = 0,
.has_interface_timing_reg = true,
.enable_hs_mode_support = false,
.has_mutex = false,
.variant = TEGRA_I2C_VARIANT_VI,
.regs = &tegra210_vi_i2c_regs,
};
#endif
static const struct tegra_i2c_hw_feature tegra186_i2c_hw = {
.has_continue_xfer_support = true,
.has_per_pkt_xfer_complete_irq = true,
@@ -1785,6 +2007,8 @@ static const struct tegra_i2c_hw_feature tegra186_i2c_hw = {
.has_interface_timing_reg = true,
.enable_hs_mode_support = false,
.has_mutex = false,
.variant = TEGRA_I2C_VARIANT_DEFAULT,
.regs = &tegra20_i2c_regs,
};
static const struct tegra_i2c_hw_feature tegra194_i2c_hw = {
@@ -1817,6 +2041,8 @@ static const struct tegra_i2c_hw_feature tegra194_i2c_hw = {
.has_interface_timing_reg = true,
.enable_hs_mode_support = true,
.has_mutex = false,
.variant = TEGRA_I2C_VARIANT_DEFAULT,
.regs = &tegra20_i2c_regs,
};
static const struct tegra_i2c_hw_feature tegra256_i2c_hw = {
@@ -1849,6 +2075,8 @@ static const struct tegra_i2c_hw_feature tegra256_i2c_hw = {
.has_interface_timing_reg = true,
.enable_hs_mode_support = true,
.has_mutex = true,
.variant = TEGRA_I2C_VARIANT_DEFAULT,
.regs = &tegra264_i2c_regs,
};
static const struct tegra_i2c_hw_feature tegra264_i2c_hw = {
@@ -1881,6 +2109,42 @@ static const struct tegra_i2c_hw_feature tegra264_i2c_hw = {
.has_interface_timing_reg = true,
.enable_hs_mode_support = true,
.has_mutex = true,
.variant = TEGRA_I2C_VARIANT_DEFAULT,
.regs = &tegra264_i2c_regs,
};
static const struct tegra_i2c_hw_feature tegra410_i2c_hw = {
.has_continue_xfer_support = true,
.has_per_pkt_xfer_complete_irq = true,
.clk_divisor_hs_mode = 1,
.clk_divisor_std_mode = 0x3f,
.clk_divisor_fast_mode = 0x2c,
.clk_divisor_fast_plus_mode = 0x11,
.has_config_load_reg = true,
.has_multi_master_mode = true,
.has_slcg_override_reg = true,
.has_mst_fifo = true,
.has_mst_reset = true,
.quirks = &tegra194_i2c_quirks,
.supports_bus_clear = true,
.has_apb_dma = false,
.tlow_std_mode = 0x8,
.thigh_std_mode = 0x7,
.tlow_fast_mode = 0x2,
.thigh_fast_mode = 0x2,
.tlow_fastplus_mode = 0x2,
.thigh_fastplus_mode = 0x2,
.tlow_hs_mode = 0x8,
.thigh_hs_mode = 0x6,
.setup_hold_time_std_mode = 0x08080808,
.setup_hold_time_fast_mode = 0x02020202,
.setup_hold_time_fastplus_mode = 0x02020202,
.setup_hold_time_hs_mode = 0x0b0b0b,
.has_interface_timing_reg = true,
.enable_hs_mode_support = true,
.has_mutex = true,
.variant = TEGRA_I2C_VARIANT_DEFAULT,
.regs = &tegra410_i2c_regs,
};
static const struct of_device_id tegra_i2c_of_match[] = {
@@ -1889,7 +2153,7 @@ static const struct of_device_id tegra_i2c_of_match[] = {
{ .compatible = "nvidia,tegra194-i2c", .data = &tegra194_i2c_hw, },
{ .compatible = "nvidia,tegra186-i2c", .data = &tegra186_i2c_hw, },
#if IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC)
{ .compatible = "nvidia,tegra210-i2c-vi", .data = &tegra210_i2c_hw, },
{ .compatible = "nvidia,tegra210-i2c-vi", .data = &tegra210_vi_i2c_hw, },
#endif
{ .compatible = "nvidia,tegra210-i2c", .data = &tegra210_i2c_hw, },
{ .compatible = "nvidia,tegra124-i2c", .data = &tegra124_i2c_hw, },
@@ -1897,7 +2161,7 @@ static const struct of_device_id tegra_i2c_of_match[] = {
{ .compatible = "nvidia,tegra30-i2c", .data = &tegra30_i2c_hw, },
{ .compatible = "nvidia,tegra20-i2c", .data = &tegra20_i2c_hw, },
#if IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC)
{ .compatible = "nvidia,tegra20-i2c-dvc", .data = &tegra20_i2c_hw, },
{ .compatible = "nvidia,tegra20-i2c-dvc", .data = &tegra20_dvc_i2c_hw, },
#endif
{},
};
@@ -1905,21 +2169,12 @@ MODULE_DEVICE_TABLE(of, tegra_i2c_of_match);
static void tegra_i2c_parse_dt(struct tegra_i2c_dev *i2c_dev)
{
struct device_node *np = i2c_dev->dev->of_node;
bool multi_mode;
i2c_parse_fw_timings(i2c_dev->dev, &i2c_dev->timings, true);
multi_mode = device_property_read_bool(i2c_dev->dev, "multi-master");
i2c_dev->multimaster_mode = multi_mode;
if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC) &&
of_device_is_compatible(np, "nvidia,tegra20-i2c-dvc"))
i2c_dev->is_dvc = true;
if (IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC) &&
of_device_is_compatible(np, "nvidia,tegra210-i2c-vi"))
i2c_dev->is_vi = true;
}
static int tegra_i2c_init_clocks(struct tegra_i2c_dev *i2c_dev)
@@ -2205,6 +2460,7 @@ static const struct acpi_device_id tegra_i2c_acpi_match[] = {
{.id = "NVDA0101", .driver_data = (kernel_ulong_t)&tegra210_i2c_hw},
{.id = "NVDA0201", .driver_data = (kernel_ulong_t)&tegra186_i2c_hw},
{.id = "NVDA0301", .driver_data = (kernel_ulong_t)&tegra194_i2c_hw},
{.id = "NVDA2017", .driver_data = (kernel_ulong_t)&tegra410_i2c_hw},
{ }
};
MODULE_DEVICE_TABLE(acpi, tegra_i2c_acpi_match);
+3 -10
View File
@@ -213,12 +213,6 @@ static int usb_write(struct i2c_adapter *adapter, int cmd,
return ret;
}
static void i2c_tiny_usb_free(struct i2c_tiny_usb *dev)
{
usb_put_dev(dev->usb_dev);
kfree(dev);
}
static int i2c_tiny_usb_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
@@ -237,7 +231,7 @@ static int i2c_tiny_usb_probe(struct usb_interface *interface,
if (!dev)
goto error;
dev->usb_dev = usb_get_dev(interface_to_usbdev(interface));
dev->usb_dev = interface_to_usbdev(interface);
dev->interface = interface;
/* save our data pointer in this interface device */
@@ -277,8 +271,7 @@ static int i2c_tiny_usb_probe(struct usb_interface *interface,
return 0;
error:
if (dev)
i2c_tiny_usb_free(dev);
kfree(dev);
return retval;
}
@@ -289,7 +282,7 @@ static void i2c_tiny_usb_disconnect(struct usb_interface *interface)
i2c_del_adapter(&dev->adapter);
usb_set_intfdata(interface, NULL);
i2c_tiny_usb_free(dev);
kfree(dev);
dev_dbg(&interface->dev, "disconnected\n");
}
+1
View File
@@ -29,6 +29,7 @@ static const struct acpi_device_id usbio_i2c_acpi_hids[] = {
{ "INTC10B6" }, /* LNL */
{ "INTC10D2" }, /* MTL-CVF */
{ "INTC10E3" }, /* PTL */
{ "INTC1118" }, /* NVL */
{ }
};
+42 -55
View File
@@ -27,7 +27,6 @@
#include <linux/platform_data/i2c-xiic.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/clk.h>
#include <linux/pm_runtime.h>
#include <linux/iopoll.h>
@@ -1408,7 +1407,6 @@ static const struct i2c_adapter xiic_adapter = {
.algo = &xiic_algorithm,
};
#if defined(CONFIG_OF)
static const struct xiic_version_data xiic_2_00 = {
.quirks = DYNAMIC_MODE_READ_BROKEN_BIT,
};
@@ -1419,28 +1417,26 @@ static const struct of_device_id xiic_of_match[] = {
{},
};
MODULE_DEVICE_TABLE(of, xiic_of_match);
#endif
static int xiic_i2c_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct fwnode_handle *fwnode = dev_fwnode(dev);
struct xiic_i2c *i2c;
struct xiic_i2c_platform_data *pdata;
const struct of_device_id *match;
const struct xiic_version_data *data;
struct resource *res;
int ret, irq;
u8 i;
u32 sr;
i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL);
if (!i2c)
return -ENOMEM;
match = of_match_node(xiic_of_match, pdev->dev.of_node);
if (match && match->data) {
const struct xiic_version_data *data = match->data;
data = device_get_match_data(dev);
if (data)
i2c->quirks = data->quirks;
}
i2c->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(i2c->base))
@@ -1450,50 +1446,52 @@ static int xiic_i2c_probe(struct platform_device *pdev)
if (irq < 0)
return irq;
pdata = dev_get_platdata(&pdev->dev);
pdata = dev_get_platdata(dev);
/* hook up driver to tree */
platform_set_drvdata(pdev, i2c);
i2c->adap = xiic_adapter;
i2c->adap.nr = pdev->id;
i2c_set_adapdata(&i2c->adap, i2c);
i2c->adap.dev.parent = &pdev->dev;
i2c->adap.dev.of_node = pdev->dev.of_node;
device_set_node(&i2c->adap.dev, fwnode);
snprintf(i2c->adap.name, sizeof(i2c->adap.name),
DRIVER_NAME " %s", pdev->name);
mutex_init(&i2c->lock);
ret = devm_mutex_init(dev, &i2c->lock);
if (ret)
return ret;
spin_lock_init(&i2c->atomic_lock);
i2c->clk = devm_clk_get_enabled(&pdev->dev, NULL);
if (IS_ERR(i2c->clk))
return dev_err_probe(&pdev->dev, PTR_ERR(i2c->clk),
"failed to enable input clock.\n");
if (is_of_node(fwnode)) {
i2c->clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(i2c->clk))
return dev_err_probe(dev, PTR_ERR(i2c->clk),
"failed to enable input clock.\n");
}
i2c->dev = &pdev->dev;
pm_runtime_set_autosuspend_delay(i2c->dev, XIIC_PM_TIMEOUT);
pm_runtime_use_autosuspend(i2c->dev);
pm_runtime_set_active(i2c->dev);
pm_runtime_enable(i2c->dev);
i2c->dev = dev;
pm_runtime_set_autosuspend_delay(dev, XIIC_PM_TIMEOUT);
pm_runtime_use_autosuspend(dev);
ret = devm_pm_runtime_set_active_enabled(dev);
if (ret)
return ret;
/* SCL frequency configuration */
i2c->input_clk = clk_get_rate(i2c->clk);
ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency",
&i2c->i2c_clk);
ret = device_property_read_u32(dev, "clock-frequency", &i2c->i2c_clk);
/* If clock-frequency not specified in DT, do not configure in SW */
if (ret || i2c->i2c_clk > I2C_MAX_FAST_MODE_PLUS_FREQ)
i2c->i2c_clk = 0;
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
xiic_process, IRQF_ONESHOT,
pdev->name, i2c);
ret = devm_request_threaded_irq(dev, irq, NULL, xiic_process,
IRQF_ONESHOT, pdev->name, i2c);
if (ret)
return ret;
if (ret < 0) {
dev_err_probe(&pdev->dev, ret, "Cannot claim IRQ\n");
goto err_pm_disable;
}
i2c->singlemaster =
of_property_read_bool(pdev->dev.of_node, "single-master");
i2c->singlemaster = device_property_read_bool(dev, "single-master");
/*
* Detect endianness
@@ -1508,16 +1506,14 @@ static int xiic_i2c_probe(struct platform_device *pdev)
i2c->endianness = BIG;
ret = xiic_reinit(i2c);
if (ret < 0) {
dev_err_probe(&pdev->dev, ret, "Cannot xiic_reinit\n");
goto err_pm_disable;
}
if (ret)
return dev_err_probe(dev, ret, "Cannot xiic_reinit\n");
/* add i2c adapter to i2c tree */
ret = i2c_add_adapter(&i2c->adap);
ret = i2c_add_numbered_adapter(&i2c->adap);
if (ret) {
xiic_deinit(i2c);
goto err_pm_disable;
return ret;
}
if (pdata) {
@@ -1526,38 +1522,29 @@ static int xiic_i2c_probe(struct platform_device *pdev)
i2c_new_client_device(&i2c->adap, pdata->devices + i);
}
dev_dbg(&pdev->dev, "mmio %08lx irq %d scl clock frequency %d\n",
(unsigned long)res->start, irq, i2c->i2c_clk);
dev_dbg(dev, "mmio %pR irq %d scl clock frequency %d\n",
res, irq, i2c->i2c_clk);
return 0;
err_pm_disable:
pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
return ret;
}
static void xiic_i2c_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct xiic_i2c *i2c = platform_get_drvdata(pdev);
int ret;
/* remove adapter & data */
i2c_del_adapter(&i2c->adap);
ret = pm_runtime_get_sync(i2c->dev);
ret = pm_runtime_get_sync(dev);
if (ret < 0)
dev_warn(&pdev->dev, "Failed to activate device for removal (%pe)\n",
dev_warn(dev, "Failed to activate device for removal (%pe)\n",
ERR_PTR(ret));
else
xiic_deinit(i2c);
pm_runtime_put_sync(i2c->dev);
pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
pm_runtime_dont_use_autosuspend(&pdev->dev);
pm_runtime_put_sync(dev);
}
static const struct dev_pm_ops xiic_dev_pm_ops = {
+4 -13
View File
@@ -49,8 +49,8 @@ struct i2c_atr_alias_pair {
* @shared: Indicates if this alias pool is shared by multiple channels
*
* @lock: Lock protecting @aliases and @use_mask
* @aliases: Array of aliases, must hold exactly @size elements
* @use_mask: Mask of used aliases
* @aliases: Array of aliases, must hold exactly @size elements
*/
struct i2c_atr_alias_pool {
size_t size;
@@ -58,8 +58,8 @@ struct i2c_atr_alias_pool {
/* Protects aliases and use_mask */
spinlock_t lock;
u16 *aliases;
unsigned long *use_mask;
u16 aliases[] __counted_by(size);
};
/**
@@ -137,22 +137,16 @@ static struct i2c_atr_alias_pool *i2c_atr_alloc_alias_pool(size_t num_aliases, b
struct i2c_atr_alias_pool *alias_pool;
int ret;
alias_pool = kzalloc_obj(*alias_pool);
alias_pool = kzalloc_flex(*alias_pool, aliases, num_aliases);
if (!alias_pool)
return ERR_PTR(-ENOMEM);
alias_pool->size = num_aliases;
alias_pool->aliases = kcalloc(num_aliases, sizeof(*alias_pool->aliases), GFP_KERNEL);
if (!alias_pool->aliases) {
ret = -ENOMEM;
goto err_free_alias_pool;
}
alias_pool->use_mask = bitmap_zalloc(num_aliases, GFP_KERNEL);
if (!alias_pool->use_mask) {
ret = -ENOMEM;
goto err_free_aliases;
goto err_free_alias_pool;
}
alias_pool->shared = shared;
@@ -161,8 +155,6 @@ static struct i2c_atr_alias_pool *i2c_atr_alloc_alias_pool(size_t num_aliases, b
return alias_pool;
err_free_aliases:
kfree(alias_pool->aliases);
err_free_alias_pool:
kfree(alias_pool);
return ERR_PTR(ret);
@@ -171,7 +163,6 @@ err_free_alias_pool:
static void i2c_atr_free_alias_pool(struct i2c_atr_alias_pool *alias_pool)
{
bitmap_free(alias_pool->use_mask);
kfree(alias_pool->aliases);
kfree(alias_pool);
}
+9
View File
@@ -206,6 +206,15 @@ config MTD_PARTITIONED_MASTER
the parent of the partition device be the master device, rather than
what lies behind the master.
config MTD_VIRT_CONCAT
bool "Virtual concatenated MTD devices"
depends on MTD_PARTITIONED_MASTER
help
The driver enables the creation of virtual MTD device by
concatenating multiple physical MTD devices into a single
entity. This allows for the creation of partitions larger than
the individual physical chips, extending across chip boundaries.
source "drivers/mtd/chips/Kconfig"
source "drivers/mtd/maps/Kconfig"
+1
View File
@@ -6,6 +6,7 @@
# Core functionality.
obj-$(CONFIG_MTD) += mtd.o
mtd-y := mtdcore.o mtdsuper.o mtdconcat.o mtdpart.o mtdchar.o
mtd-$(CONFIG_MTD_VIRT_CONCAT) += mtd_virt_concat.o
obj-y += parsers/
+1 -2
View File
@@ -2049,7 +2049,6 @@ err_probe:
static void docg3_release(struct platform_device *pdev)
{
struct docg3_cascade *cascade = platform_get_drvdata(pdev);
struct docg3 *docg3 = cascade->floors[0]->priv;
int floor;
doc_unregister_sysfs(pdev, cascade);
@@ -2057,7 +2056,7 @@ static void docg3_release(struct platform_device *pdev)
if (cascade->floors[floor])
doc_release_device(cascade->floors[floor]);
bch_free(docg3->cascade->bch);
bch_free(cascade->bch);
}
#ifdef CONFIG_OF
-11
View File
@@ -75,17 +75,6 @@ config MTD_PHYSMAP_OF
physically into the CPU's memory. The mapping description here is
taken from OF device tree.
config MTD_PHYSMAP_BT1_ROM
bool "Baikal-T1 Boot ROMs OF-based physical memory map handling"
depends on MTD_PHYSMAP_OF
depends on MIPS_BAIKAL_T1 || COMPILE_TEST
select MTD_COMPLEX_MAPPINGS
select MULTIPLEXER
select MUX_MMIO
help
This provides some extra DT physmap parsing for the Baikal-T1
platforms, some detection and setting up ROMs-specific accessors.
config MTD_PHYSMAP_VERSATILE
bool "ARM Versatile OF-based physical memory map handling"
depends on MTD_PHYSMAP_OF
-1
View File
@@ -19,7 +19,6 @@ obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o
obj-$(CONFIG_MTD_PXA2XX) += pxa2xx-flash.o
obj-$(CONFIG_MTD_PHYSMAP) += physmap.o
physmap-y := physmap-core.o
physmap-$(CONFIG_MTD_PHYSMAP_BT1_ROM) += physmap-bt1-rom.o
physmap-$(CONFIG_MTD_PHYSMAP_VERSATILE) += physmap-versatile.o
physmap-$(CONFIG_MTD_PHYSMAP_GEMINI) += physmap-gemini.o
physmap-$(CONFIG_MTD_PHYSMAP_IXP4XX) += physmap-ixp4xx.o
-125
View File
@@ -1,125 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
*
* Authors:
* Serge Semin <Sergey.Semin@baikalelectronics.ru>
*
* Baikal-T1 Physically Mapped Internal ROM driver
*/
#include <linux/bits.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/mtd/map.h>
#include <linux/mtd/xip.h>
#include <linux/mux/consumer.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/string.h>
#include <linux/types.h>
#include "physmap-bt1-rom.h"
/*
* Baikal-T1 SoC ROMs are only accessible by the dword-aligned instructions.
* We have to take this into account when implementing the data read-methods.
* Note there is no need in bothering with endianness, since both Baikal-T1
* CPU and MMIO are LE.
*/
static map_word __xipram bt1_rom_map_read(struct map_info *map,
unsigned long ofs)
{
void __iomem *src = map->virt + ofs;
unsigned int shift;
map_word ret;
u32 data;
/* Read data within offset dword. */
shift = (uintptr_t)src & 0x3;
data = readl_relaxed(src - shift);
if (!shift) {
ret.x[0] = data;
return ret;
}
ret.x[0] = data >> (shift * BITS_PER_BYTE);
/* Read data from the next dword. */
shift = 4 - shift;
if (ofs + shift >= map->size)
return ret;
data = readl_relaxed(src + shift);
ret.x[0] |= data << (shift * BITS_PER_BYTE);
return ret;
}
static void __xipram bt1_rom_map_copy_from(struct map_info *map,
void *to, unsigned long from,
ssize_t len)
{
void __iomem *src = map->virt + from;
unsigned int shift, chunk;
u32 data;
if (len <= 0 || from >= map->size)
return;
/* Make sure we don't go over the map limit. */
len = min_t(ssize_t, map->size - from, len);
/*
* Since requested data size can be pretty big we have to implement
* the copy procedure as optimal as possible. That's why it's split
* up into the next three stages: unaligned head, aligned body,
* unaligned tail.
*/
shift = (uintptr_t)src & 0x3;
if (shift) {
chunk = min_t(ssize_t, 4 - shift, len);
data = readl_relaxed(src - shift);
memcpy(to, (char *)&data + shift, chunk);
src += chunk;
to += chunk;
len -= chunk;
}
while (len >= 4) {
data = readl_relaxed(src);
memcpy(to, &data, 4);
src += 4;
to += 4;
len -= 4;
}
if (len) {
data = readl_relaxed(src);
memcpy(to, &data, len);
}
}
int of_flash_probe_bt1_rom(struct platform_device *pdev,
struct device_node *np,
struct map_info *map)
{
struct device *dev = &pdev->dev;
/* It's supposed to be read-only MTD. */
if (!of_device_is_compatible(np, "mtd-rom")) {
dev_info(dev, "No mtd-rom compatible string\n");
return 0;
}
/* Multiplatform guard. */
if (!of_device_is_compatible(np, "baikal,bt1-int-rom"))
return 0;
/* Sanity check the device parameters retrieved from DTB. */
if (map->bankwidth != 4)
dev_warn(dev, "Bank width is supposed to be 32 bits wide\n");
map->read = bt1_rom_map_read;
map->copy_from = bt1_rom_map_copy_from;
return 0;
}
-17
View File
@@ -1,17 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#include <linux/mtd/map.h>
#include <linux/of.h>
#ifdef CONFIG_MTD_PHYSMAP_BT1_ROM
int of_flash_probe_bt1_rom(struct platform_device *pdev,
struct device_node *np,
struct map_info *map);
#else
static inline
int of_flash_probe_bt1_rom(struct platform_device *pdev,
struct device_node *np,
struct map_info *map)
{
return 0;
}
#endif
-5
View File
@@ -42,7 +42,6 @@
#include <linux/pm_runtime.h>
#include <linux/gpio/consumer.h>
#include "physmap-bt1-rom.h"
#include "physmap-gemini.h"
#include "physmap-ixp4xx.h"
#include "physmap-versatile.h"
@@ -365,10 +364,6 @@ static int physmap_flash_of_init(struct platform_device *dev)
info->maps[i].bankwidth = bankwidth;
info->maps[i].device_node = dp;
err = of_flash_probe_bt1_rom(dev, dp, &info->maps[i]);
if (err)
return err;
err = of_flash_probe_gemini(dev, dp, &info->maps[i]);
if (err)
return err;
+1 -1
View File
@@ -181,7 +181,7 @@ int of_flash_probe_gemini(struct platform_device *pdev,
dev_err(dev, "no enabled pin control state\n");
gf->disabled_state = pinctrl_lookup_state(gf->p, "disabled");
if (IS_ERR(gf->enabled_state)) {
if (IS_ERR(gf->disabled_state)) {
dev_err(dev, "no disabled pin control state\n");
} else {
ret = pinctrl_select_state(gf->p, gf->disabled_state);
+350
View File
@@ -0,0 +1,350 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Virtual concat MTD device driver
*
* Copyright (C) 2018 Bernhard Frauendienst
* Author: Bernhard Frauendienst <kernel@nospam.obeliks.de>
*/
#include <linux/device.h>
#include <linux/mtd/mtd.h>
#include "mtdcore.h"
#include <linux/mtd/partitions.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/slab.h>
#include <linux/mtd/concat.h>
#define CONCAT_PROP "part-concat-next"
#define CONCAT_POSTFIX "concat"
#define MIN_DEV_PER_CONCAT 1
static LIST_HEAD(concat_node_list);
/**
* struct mtd_virt_concat_node - components of a concatenation
* @head: List handle
* @count: Number of nodes
* @nodes: Pointer to the nodes (partitions) to concatenate
* @concat: Concatenation container
*/
struct mtd_virt_concat_node {
struct list_head head;
unsigned int count;
struct mtd_concat *concat;
struct device_node *nodes[] __counted_by(count);
};
/**
* mtd_is_part_concat - Check if the device is already part
* of a concatenated device
* @dev: pointer to 'device_node'
*
* Return: true if the device is already part of a concatenation,
* false otherwise.
*/
static bool mtd_is_part_concat(struct device_node *dev)
{
struct mtd_virt_concat_node *item;
int idx;
list_for_each_entry(item, &concat_node_list, head) {
for (idx = 0; idx < item->count; idx++) {
if (item->nodes[idx] == dev)
return true;
}
}
return false;
}
static void mtd_virt_concat_put_mtd_devices(struct mtd_concat *concat)
{
int i;
for (i = 0; i < concat->num_subdev; i++)
put_mtd_device(concat->subdev[i]);
}
void mtd_virt_concat_destroy_joins(void)
{
struct mtd_virt_concat_node *item, *tmp;
struct mtd_info *mtd;
list_for_each_entry_safe(item, tmp, &concat_node_list, head) {
mtd = &item->concat->mtd;
if (item->concat) {
mtd_device_unregister(mtd);
kfree(mtd->name);
mtd_concat_destroy(mtd);
mtd_virt_concat_put_mtd_devices(item->concat);
}
}
}
/**
* mtd_virt_concat_destroy - Destroy the concat that includes the mtd object
* @mtd: pointer to 'mtd_info'
*
* Return: 0 on success, -error otherwise.
*/
int mtd_virt_concat_destroy(struct mtd_info *mtd)
{
struct mtd_info *child, *master = mtd_get_master(mtd);
struct mtd_virt_concat_node *item, *tmp;
struct mtd_concat *concat;
int idx, ret = 0;
bool is_mtd_found;
list_for_each_entry_safe(item, tmp, &concat_node_list, head) {
is_mtd_found = false;
/* Find the concat item that hold the mtd device */
for (idx = 0; idx < item->count; idx++) {
if (item->nodes[idx] == mtd->dev.of_node) {
is_mtd_found = true;
break;
}
}
if (!is_mtd_found)
continue;
concat = item->concat;
/*
* Since this concatenated device is being removed, retrieve
* all MTD devices that are part of it and register them
* individually.
*/
for (idx = 0; idx < concat->num_subdev; idx++) {
child = concat->subdev[idx];
if (child->dev.of_node != mtd->dev.of_node) {
ret = add_mtd_device(child);
if (ret)
goto out;
}
}
/* Destroy the concat */
if (concat->mtd.name) {
del_mtd_device(&concat->mtd);
kfree(concat->mtd.name);
mtd_concat_destroy(&concat->mtd);
mtd_virt_concat_put_mtd_devices(item->concat);
}
for (idx = 0; idx < item->count; idx++)
of_node_put(item->nodes[idx]);
kfree(item);
}
return 0;
out:
mutex_lock(&master->master.partitions_lock);
list_del(&child->part.node);
mutex_unlock(&master->master.partitions_lock);
kfree(mtd->name);
kfree(mtd);
return ret;
}
/**
* mtd_virt_concat_create_item - Create a concat item
* @parts: pointer to 'device_node'
* @count: number of mtd devices that make up
* the concatenated device.
*
* Return: 0 on success, -error otherwise.
*/
static int mtd_virt_concat_create_item(struct device_node *parts,
unsigned int count)
{
struct mtd_virt_concat_node *item;
struct mtd_concat *concat;
int i;
for (i = 0; i < (count - 1); i++) {
if (mtd_is_part_concat(of_parse_phandle(parts, CONCAT_PROP, i)))
return 0;
}
item = kzalloc_flex(*item, nodes, count, GFP_KERNEL);
if (!item)
return -ENOMEM;
item->count = count;
/*
* The partition in which "part-concat-next" property
* is defined is the first device in the list of concat
* devices.
*/
item->nodes[0] = parts;
for (i = 1; i < count; i++)
item->nodes[i] = of_parse_phandle(parts, CONCAT_PROP, (i - 1));
concat = kzalloc_flex(*concat, subdev, count, GFP_KERNEL);
if (!concat) {
kfree(item);
return -ENOMEM;
}
item->concat = concat;
list_add_tail(&item->head, &concat_node_list);
return 0;
}
void mtd_virt_concat_destroy_items(void)
{
struct mtd_virt_concat_node *item, *temp;
int i;
list_for_each_entry_safe(item, temp, &concat_node_list, head) {
for (i = 0; i < item->count; i++)
of_node_put(item->nodes[i]);
kfree(item);
}
}
/**
* mtd_virt_concat_add - Add a mtd device to the concat list
* @mtd: pointer to 'mtd_info'
*
* Return: true on success, false otherwise.
*/
bool mtd_virt_concat_add(struct mtd_info *mtd)
{
struct mtd_virt_concat_node *item;
struct mtd_concat *concat;
int idx;
list_for_each_entry(item, &concat_node_list, head) {
concat = item->concat;
for (idx = 0; idx < item->count; idx++) {
if (item->nodes[idx] == mtd->dev.of_node) {
concat->subdev[concat->num_subdev++] = mtd;
return true;
}
}
}
return false;
}
/**
* mtd_virt_concat_node_create - List all the concatenations found in DT
*
* Return: 0 on success, -error otherwise.
*/
int mtd_virt_concat_node_create(void)
{
struct device_node *parts = NULL;
int ret = 0, count = 0;
/* List all the concatenations found in DT */
do {
parts = of_find_node_with_property(parts, CONCAT_PROP);
if (!of_device_is_available(parts))
continue;
if (mtd_is_part_concat(parts))
continue;
count = of_count_phandle_with_args(parts, CONCAT_PROP, NULL);
if (count < MIN_DEV_PER_CONCAT)
continue;
/*
* The partition in which "part-concat-next" property is defined
* is also part of the concat device, so increament count by 1.
*/
count++;
ret = mtd_virt_concat_create_item(parts, count);
if (ret) {
of_node_put(parts);
goto destroy_items;
}
} while (parts);
return ret;
destroy_items:
mtd_virt_concat_destroy_items();
return ret;
}
/**
* mtd_virt_concat_create_join - Create and register the concatenated
* MTD device.
*
* Return: 0 on success, -error otherwise.
*/
int mtd_virt_concat_create_join(void)
{
struct mtd_virt_concat_node *item;
struct mtd_concat *concat;
struct mtd_info *mtd;
ssize_t name_sz;
int ret, idx;
char *name;
list_for_each_entry(item, &concat_node_list, head) {
concat = item->concat;
/*
* Check if item->count != concat->num_subdev, it indicates
* that the MTD information for all devices included in the
* concatenation are not handy, concat MTD device can't be
* created hence switch to next concat device.
*/
if (item->count != concat->num_subdev) {
continue;
} else {
/* Calculate the legth of the name of the virtual device */
for (idx = 0, name_sz = 0; idx < concat->num_subdev; idx++)
name_sz += (strlen(concat->subdev[idx]->name) + 1);
name_sz += strlen(CONCAT_POSTFIX);
name = kmalloc(name_sz + 1, GFP_KERNEL);
if (!name) {
mtd_virt_concat_put_mtd_devices(concat);
return -ENOMEM;
}
ret = 0;
for (idx = 0; idx < concat->num_subdev; idx++) {
ret += sprintf((name + ret), "%s-",
concat->subdev[idx]->name);
}
sprintf((name + ret), CONCAT_POSTFIX);
if (concat->mtd.name) {
ret = memcmp(concat->mtd.name, name, name_sz);
if (ret == 0)
continue;
}
mtd = mtd_concat_create(concat->subdev, concat->num_subdev, name);
if (!mtd) {
kfree(name);
return -ENXIO;
}
concat->mtd = *mtd;
/* Arbitrary set the first device as parent */
concat->mtd.dev.parent = concat->subdev[0]->dev.parent;
concat->mtd.dev = concat->subdev[0]->dev;
/* Add the mtd device */
ret = add_mtd_device(&concat->mtd);
if (ret)
goto destroy_concat;
}
}
return 0;
destroy_concat:
mtd_concat_destroy(mtd);
return ret;
}
+1 -16
View File
@@ -20,18 +20,6 @@
#include <asm/div64.h>
/*
* Our storage structure:
* Subdev points to an array of pointers to struct mtd_info objects
* which is allocated along with this structure
*
*/
struct mtd_concat {
struct mtd_info mtd;
int num_subdev;
struct mtd_info **subdev;
};
/*
* how to calculate the size required for the above structure,
* including the pointer array subdev points to:
@@ -639,7 +627,6 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
const char *name)
{ /* name for the new device */
int i;
size_t size;
struct mtd_concat *concat;
struct mtd_info *subdev_master = NULL;
uint32_t max_erasesize, curr_erasesize;
@@ -652,15 +639,13 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
printk(KERN_NOTICE "into device \"%s\"\n", name);
/* allocate the device structure */
size = SIZEOF_STRUCT_MTD_CONCAT(num_devs);
concat = kzalloc(size, GFP_KERNEL);
concat = kzalloc_flex(*concat, subdev, num_devs, GFP_KERNEL);
if (!concat) {
printk
("memory allocation error while creating concatenated device \"%s\"\n",
name);
return NULL;
}
concat->subdev = (struct mtd_info **) (concat + 1);
/*
* Set up the new "super" device's MTD object structure, check for
+21
View File
@@ -34,6 +34,7 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/concat.h>
#include "mtdcore.h"
@@ -1120,6 +1121,12 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types,
goto out;
}
if (IS_REACHABLE(CONFIG_MTD_VIRT_CONCAT)) {
ret = mtd_virt_concat_node_create();
if (ret < 0)
goto out;
}
/* Prefer parsed partitions over driver-provided fallback */
ret = parse_mtd_partitions(mtd, types, parser_data);
if (ret == -EPROBE_DEFER)
@@ -1137,6 +1144,11 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types,
if (ret)
goto out;
if (IS_REACHABLE(CONFIG_MTD_VIRT_CONCAT)) {
ret = mtd_virt_concat_create_join();
if (ret < 0)
goto out;
}
/*
* FIXME: some drivers unfortunately call this function more than once.
* So we have to check if we've already assigned the reboot notifier.
@@ -1186,6 +1198,11 @@ int mtd_device_unregister(struct mtd_info *master)
nvmem_unregister(master->otp_user_nvmem);
nvmem_unregister(master->otp_factory_nvmem);
if (IS_REACHABLE(CONFIG_MTD_VIRT_CONCAT)) {
err = mtd_virt_concat_destroy(master);
if (err)
return err;
}
err = del_mtd_partitions(master);
if (err)
return err;
@@ -2621,6 +2638,10 @@ err_reg:
static void __exit cleanup_mtd(void)
{
if (IS_REACHABLE(CONFIG_MTD_VIRT_CONCAT)) {
mtd_virt_concat_destroy_joins();
mtd_virt_concat_destroy_items();
}
debugfs_remove_recursive(dfs_dir_mtd);
cleanup_mtdchar();
if (proc_mtd)
Loaded 100 of 505 files, more files were not shown because too many files have changed in this diff. Show more