Merge 2779759c09 ("Merge tag 'i2c-7.2-part1' of git://git.kernel.org/pub/scm/linux/kernel/git/andi.shyti/linux") into android-mainline

Steps on the way to v7.2-rc1

Change-Id: I89756ff39d88d043ad499ade587b0993c13c26c6
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-07-28 09:09:00 -07:00
commit c41403771c
184 files changed
+4840 -2088

No files matched your search

@@ -0,0 +1,26 @@
What: /sys/devices/faux/tdx_host/version
Contact: linux-coco@lists.linux.dev
Description: (RO) Report the version of the loaded TDX module.
Formatted as "major.minor.update". Used by TDX module
update tooling. Example: "1.2.03".
What: /sys/devices/faux/tdx_host/seamldr_version
Contact: linux-coco@lists.linux.dev
Description: (RO) Report the version of the loaded P-SEAMLDR.
Formatted as a TDX module version. Used by TDX module
update tooling.
What: /sys/devices/faux/tdx_host/num_remaining_updates
Contact: linux-coco@lists.linux.dev
Description: (RO) Report the number of remaining updates. TDX maintains a
log about each TDX module that has been loaded. This log has
a finite size, which limits the number of TDX module updates
that can be performed.
After each successful update, the number reduces by one. Once it
reaches zero, further updates will fail until next reboot. The
number is always zero if the P-SEAMLDR doesn't support updates.
See Intel Trust Domain Extensions - SEAM Loader (SEAMLDR)
Interface Specification, Chapter "SEAMLDR_INFO" and Chapter
"SEAMLDR.INSTALL" for more information.
+127 -7
View File
@@ -73,6 +73,133 @@ initialize::
[..] virt/tdx: TDX-Module initialization failed ...
TDX module Runtime Update
-------------------------
Similar to microcode, the BIOS generally has a copy of the TDX module
in flash. It loads this module image in to RAM at boot. However, just
like microcode, the BIOS-loaded TDX module might be out of date either
because the BIOS is old or the system has been up a long time. The
kernel can replace the BIOS version in RAM and load a different TDX
module. Kernel-loaded TDX modules do not affect the BIOS flash and do
not survive reboots.
The TDX module is normally the only piece of software running in SEAM
mode with which the kernel interacts. But there is a second piece of
software which is used to load or update the TDX module: a persistent
SEAM loader (P-SEAMLDR). It runs in SEAM mode separately from the TDX
module. The kernel communicates with the P-SEAMLDR to perform TDX
module runtime updates.
How to update the TDX module
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Updating the TDX module is a complex process. Much of the logic and
policy is left to userspace. End users should use existing update
infrastructure provided by their distro. The Intel TDX Module Binaries
repository has a reference implementation of this logic:
https://github.com/intel/confidential-computing.tdx.tdx-module.binaries/blob/main/version_select_and_load.py
This section will now lay out roughly what is needed to implement a
userspace-driven TDX module update. Detailed documentation on the
tdx_host ABIs is available here::
Documentation/ABI/testing/sysfs-devices-faux-tdx-host
and is not duplicated in this document.
1. Check whether runtime update is supported at all
Verify that the TDX module firmware upload interface is available::
/sys/class/firmware/tdx_module
Note that this is the generic kernel firmware update ABI. It is
separate from the "tdx_host" device ABI itself.
2. Check whether additional updates are possible. Verify that::
/sys/devices/faux/tdx_host/num_remaining_updates
has a value greater than 0. If it is 0, the TDX update log might be
full. Reboot to reset this to a nonzero value.
3. Choose a compatible TDX module image
Choosing a compatible TDX module image is not trivial. There are both
hard compatibility requirements and policy choices to make.
Hard compatibility requirements:
- The update must be compatible with the kernel.
The update must not change any TDX ABIs in any non-backward-compatible
way. It can introduce new features but must not require that the kernel
use new ABIs for existing features. It must ensure that the rest of the
system is not affected in any way. Software on the system must never
notice any behavioral changes. Attestation results should be identical
except for version changes.
- The update must be compatible with the CPU.
The set of supported CPU FMS values (family, model, stepping) is
encoded in the module image itself. In practice, module version series
are platform-specific. For example, the 1.5.x series runs on Sapphire
Rapids but not Granite Rapids, which needs 2.0.x.
- The update must be compatible with the P-SEAMLDR.
This information is provided in a metadata file, typically
mapping_file.json, released with the module image. Each module image
specifies the minimum required P-SEAMLDR version, and the update is
compatible only if the running P-SEAMLDR meets that requirement.
The current version of the P-SEAMLDR can be read here::
/sys/devices/faux/tdx_host/seamldr_version
- The update must be compatible with the running TDX module.
Like P-SEAMLDR, each module image also specifies a minimum required
TDX module version. The running module must satisfy that requirement.
The update software can read the current TDX module version here::
/sys/devices/faux/tdx_host/version
Policy choices:
- The update software chooses how to optimize its update. For instance,
it can optimize for fewer updates or for smaller version steps,
for example, 1.2.3 => 1.2.5 versus 1.2.3 => 1.2.4 => 1.2.5.
4. Perform the update
Run::
echo 1 > /sys/class/firmware/tdx_module/loading
cat <path_to_module_image> > /sys/class/firmware/tdx_module/data
echo 0 > /sys/class/firmware/tdx_module/loading
The files /sys/class/firmware/tdx_module/status and
/sys/class/firmware/tdx_module/error report update progress and error
information.
After the update completes, the new module version is visible in
/sys/devices/faux/tdx_host/version.
Impact on running TDs
~~~~~~~~~~~~~~~~~~~~~
TDX module runtime updates must have virtually no visible impact on running
TDs. Any TD visible impact is a TDX module bug.
The main exception is the TEE_TCB_SVN_2 field in TD quotes, which
reflects the TCB of the currently running TDX module and therefore
changes after an update. By contrast, TEE_TCB_SVN reflects the TCB at TD
launch time and is not affected.
TDX Interaction to Other Kernel Components
------------------------------------------
@@ -138,13 +265,6 @@ If the platform has such erratum, the kernel prints additional message in
machine check handler to tell user the machine check may be caused by
kernel bug on TDX private memory.
Kexec
~~~~~~~
Currently kexec doesn't work on the TDX platforms with the aforementioned
erratum. It fails when loading the kexec kernel image. Otherwise it
works normally.
Interaction vs S3 and deeper states
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -45,6 +45,18 @@ properties:
$ref: /schemas/types.yaml#/definitions/uint32
description: Number of daisy-chained shift registers
lines-initial-states:
$ref: /schemas/types.yaml#/definitions/uint32
description:
Bitmask that specifies the initial state of each output line, written
by the driver before the gpiochip is registered. Bit N corresponds to
GPIO line N, following the convention already documented for
nxp,pcf8575. Because the 74HC595/74LVC594 family is push-pull output
only, a bit set to zero drives the line low and a bit set to one
drives it high. The bitmask covers up to 32 lines (four cascaded
registers); outputs beyond that come up zeroed. When the property is
absent all outputs come up low, preserving the previous behaviour.
enable-gpios:
description: GPIO connected to the OE (Output Enable) pin.
maxItems: 1
@@ -79,6 +91,7 @@ examples:
gpio-controller;
#gpio-cells = <2>;
registers-number = <4>;
lines-initial-states = <0xffff0000>;
spi-max-frequency = <100000>;
};
};
@@ -12,10 +12,11 @@ maintainers:
properties:
compatible:
enum:
- xlnx,eio-gpio-1.0
- xlnx,pmc-gpio-1.0
- xlnx,versal-gpio-1.0
- xlnx,zynq-gpio-1.0
- xlnx,zynqmp-gpio-1.0
- xlnx,versal-gpio-1.0
- xlnx,pmc-gpio-1.0
reg:
maxItems: 1
@@ -30,7 +31,7 @@ properties:
gpio-line-names:
description: strings describing the names of each gpio line
minItems: 58
minItems: 52
maxItems: 174
interrupt-controller: true
@@ -89,6 +90,16 @@ allOf:
minItems: 116
maxItems: 116
- if:
properties:
compatible:
enum:
- xlnx,eio-gpio-1.0
then:
properties:
gpio-line-names:
maxItems: 52
required:
- compatible
- reg
@@ -85,6 +85,8 @@ properties:
- nvidia,tegra194-gpio-aon
- nvidia,tegra234-gpio
- nvidia,tegra234-gpio-aon
- nvidia,tegra238-gpio
- nvidia,tegra238-gpio-aon
- nvidia,tegra256-gpio
- nvidia,tegra264-gpio
- nvidia,tegra264-gpio-uphy
@@ -163,6 +165,7 @@ allOf:
- nvidia,tegra186-gpio
- nvidia,tegra194-gpio
- nvidia,tegra234-gpio
- nvidia,tegra238-gpio
- nvidia,tegra256-gpio
- nvidia,tegra264-gpio
- nvidia,tegra264-gpio-uphy
@@ -180,6 +183,7 @@ allOf:
- nvidia,tegra186-gpio-aon
- nvidia,tegra194-gpio-aon
- nvidia,tegra234-gpio-aon
- nvidia,tegra238-gpio-aon
- nvidia,tegra264-gpio-aon
then:
properties:
@@ -192,6 +196,8 @@ allOf:
compatible:
contains:
enum:
- nvidia,tegra238-gpio
- nvidia,tegra238-gpio-aon
- nvidia,tegra264-gpio
- nvidia,tegra264-gpio-uphy
- nvidia,tegra264-gpio-aon
@@ -95,6 +95,12 @@ patternProperties:
'#interrupt-cells':
const: 2
patternProperties:
"^.+-hog(-[0-9]+)?$":
type: object
required:
- gpio-hog
required:
- compatible
- reg
@@ -0,0 +1,73 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/gpio/waveshare,dsi-touch-gpio.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Waveshare GPIO controller on DSI TOUCH panels
maintainers:
- Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
description:
Waveshare DSI TOUCH panel kits contain separate GPIO controller for toggling
power supplies and panel / touchscreen resets.
properties:
compatible:
const: waveshare,dsi-touch-gpio
reg:
maxItems: 1
gpio-controller: true
'#gpio-cells':
const: 2
required:
- compatible
- reg
- gpio-controller
- "#gpio-cells"
additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
wsgpio: gpio@45 {
compatible = "waveshare,dsi-touch-gpio";
reg = <0x45>;
gpio-controller;
#gpio-cells = <2>;
};
};
panel_avdd: regulator-panel-avdd {
compatible = "regulator-fixed";
regulator-name = "panel-avdd";
gpios = <&wsgpio 0 GPIO_ACTIVE_HIGH>;
enable-active-high;
};
panel_iovcc: regulator-panel-iovcc {
compatible = "regulator-fixed";
regulator-name = "panel-iovcc";
gpios = <&wsgpio 4 GPIO_ACTIVE_HIGH>;
enable-active-high;
};
panel_vcc: regulator-panel-vcc {
compatible = "regulator-fixed";
regulator-name = "panel-vcc";
gpios = <&wsgpio 8 GPIO_ACTIVE_HIGH>;
enable-active-high;
regulator-always-on;
};
...
@@ -1,43 +0,0 @@
* Texas Instruments Davinci/Keystone I2C
This file provides information, what the device node for the
davinci/keystone i2c interface contains.
Required properties:
- compatible: "ti,davinci-i2c" or "ti,keystone-i2c";
- reg : Offset and length of the register set for the device
- clocks: I2C functional clock phandle.
For 66AK2G this property should be set per binding,
Documentation/devicetree/bindings/clock/ti,sci-clk.yaml
SoC-specific Required Properties:
The following are mandatory properties for Keystone 2 66AK2G SoCs only:
- power-domains: Should contain a phandle to a PM domain provider node
and an args specifier containing the I2C device id
value. This property is as per the binding,
Documentation/devicetree/bindings/soc/ti/sci-pm-domain.yaml
Recommended properties :
- interrupts : standard interrupt property.
- clock-frequency : desired I2C bus clock frequency in Hz.
- ti,has-pfunc: boolean; if defined, it indicates that SoC supports PFUNC
registers. PFUNC registers allow to switch I2C pins to function as
GPIOs, so they can be toggled manually.
Example (enbw_cmc board):
i2c@1c22000 {
compatible = "ti,davinci-i2c";
reg = <0x22000 0x1000>;
clock-frequency = <100000>;
interrupts = <15>;
interrupt-parent = <&intc>;
#address-cells = <1>;
#size-cells = <0>;
dtt@48 {
compatible = "national,lm75";
reg = <0x48>;
};
};
@@ -16,6 +16,7 @@ properties:
compatible:
enum:
- loongson,ls2k-i2c
- loongson,ls2k0300-i2c
- loongson,ls7a-i2c
reg:
@@ -24,6 +25,9 @@ properties:
interrupts:
maxItems: 1
clocks:
maxItems: 1
required:
- compatible
- reg
@@ -26,6 +26,7 @@ properties:
- items:
- enum:
- qcom,glymur-cci
- qcom,kaanapali-cci
- qcom,milos-cci
- qcom,qcm2290-cci
@@ -35,6 +36,7 @@ properties:
- qcom,sc8280xp-cci
- qcom,sdm670-cci
- qcom,sdm845-cci
- qcom,shikra-cci
- qcom,sm6150-cci
- qcom,sm6350-cci
- qcom,sm8250-cci
@@ -134,9 +136,11 @@ allOf:
compatible:
contains:
enum:
- qcom,glymur-cci
- qcom,kaanapali-cci
- qcom,qcm2290-cci
- qcom,qcs8300-cci
- qcom,shikra-cci
- qcom,sm8750-cci
then:
properties:
@@ -0,0 +1,70 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/i2c/ti,davinci-i2c.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Texas Instruments DaVinci/Keystone I2C
maintainers:
- Chaitanya Sabnis <chaitanya.msabnis@gmail.com>
allOf:
- $ref: /schemas/i2c/i2c-controller.yaml#
- if:
properties:
compatible:
contains:
const: ti,keystone-i2c
then:
required:
- power-domains
properties:
compatible:
enum:
- ti,davinci-i2c
- ti,keystone-i2c
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
maxItems: 1
power-domains:
maxItems: 1
ti,has-pfunc:
description:
Indicates that the SoC supports PFUNC registers, allowing I2C pins
to function as GPIOs for manual toggling.
type: boolean
required:
- compatible
- reg
- interrupts
- clocks
unevaluatedProperties: false
examples:
- |
i2c@1c22000 {
compatible = "ti,davinci-i2c";
reg = <0x01c22000 0x1000>;
clocks = <&i2c_clk>;
clock-frequency = <100000>;
interrupts = <15>;
#address-cells = <1>;
#size-cells = <0>;
sensor@48 {
compatible = "national,lm75";
reg = <0x48>;
};
};
@@ -15,7 +15,9 @@ allOf:
properties:
compatible:
contains:
const: spacemit,k1-pwm
enum:
- spacemit,k1-pwm
- spacemit,k3-pwm
then:
properties:
"#pwm-cells":
@@ -26,6 +28,26 @@ allOf:
const: 1
description: |
Used for specifying the period length in nanoseconds.
- if:
properties:
compatible:
contains:
enum:
- spacemit,k3-pwm
then:
required:
- clock-names
properties:
clocks:
minItems: 2
clock-names:
minItems: 2
else:
properties:
clocks:
maxItems: 1
clock-names:
maxItems: 1
properties:
compatible:
@@ -36,7 +58,9 @@ properties:
- marvell,pxa168-pwm
- marvell,pxa910-pwm
- items:
- const: spacemit,k1-pwm
- enum:
- spacemit,k1-pwm
- spacemit,k3-pwm
- const: marvell,pxa910-pwm
reg:
@@ -47,7 +71,18 @@ properties:
description: Number of cells in a pwm specifier.
clocks:
maxItems: 1
minItems: 1
items:
- description: The function clock
- description: An optional bus clock
clock-names:
minItems: 1
maxItems: 2
oneOf:
- items:
- const: func
- const: bus
resets:
maxItems: 1
@@ -1,18 +0,0 @@
== ST STMPE PWM controller ==
This is a PWM block embedded in the ST Microelectronics STMPE
(ST Multi-Purpose Expander) chips. The PWM is registered as a
subdevices of the STMPE MFD device.
Required properties:
- compatible: should be:
- "st,stmpe-pwm"
- #pwm-cells: should be 2. See pwm.yaml in this directory for a description of
the cells format.
Example:
pwm0: pwm {
compatible = "st,stmpe-pwm";
#pwm-cells = <2>;
};
+3 -2
View File
@@ -15077,6 +15077,7 @@ M: Binbin Zhou <zhoubinbin@loongson.cn>
L: linux-i2c@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/i2c/loongson,ls2x-i2c.yaml
F: drivers/i2c/busses/i2c-ls2x-v2.c
F: drivers/i2c/busses/i2c-ls2x.c
LOONGSON PWM DRIVER
@@ -26718,7 +26719,7 @@ M: Bartosz Golaszewski <brgl@kernel.org>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux.git
F: Documentation/devicetree/bindings/i2c/i2c-davinci.txt
F: Documentation/devicetree/bindings/i2c/ti,davinci-i2c.yaml
F: arch/arm/boot/dts/ti/davinci/
F: arch/arm/mach-davinci/
F: drivers/i2c/busses/i2c-davinci.c
@@ -28985,8 +28986,8 @@ F: arch/x86/kernel/unwind_*.c
X86 TRUST DOMAIN EXTENSIONS (TDX)
M: Kiryl Shutsemau <kas@kernel.org>
M: Rick Edgecombe <rick.p.edgecombe@intel.com>
R: Dave Hansen <dave.hansen@linux.intel.com>
R: Rick Edgecombe <rick.p.edgecombe@intel.com>
L: x86@kernel.org
L: linux-coco@lists.linux.dev
L: kvm@vger.kernel.org
+5 -28
View File
@@ -36,27 +36,6 @@
#include "clock.h"
#include "mmc.h"
static const struct software_node nokia770_mpuio_gpiochip_node = {
.name = "mpuio",
};
static const struct software_node nokia770_gpiochip1_node = {
.name = "gpio-0-15",
};
static const struct software_node nokia770_gpiochip2_node = {
.name = "gpio-16-31",
};
static const struct software_node *nokia770_gpiochip_nodes[] = {
&nokia770_mpuio_gpiochip_node,
&nokia770_gpiochip1_node,
&nokia770_gpiochip2_node,
NULL
};
#define ADS7846_PENDOWN_GPIO 15
static const unsigned int nokia770_keymap[] = {
KEY(1, 0, GROUP_0 | KEY_UP),
KEY(2, 0, GROUP_1 | KEY_F5),
@@ -112,7 +91,7 @@ static const struct omap_lcd_config nokia770_lcd_config __initconst = {
};
static const struct property_entry nokia770_mipid_props[] = {
PROPERTY_ENTRY_GPIO("reset-gpios", &nokia770_gpiochip1_node,
PROPERTY_ENTRY_GPIO("reset-gpios", &omap16xx_gpio1_swnode,
13, GPIO_ACTIVE_LOW),
{ }
};
@@ -138,8 +117,7 @@ static const struct property_entry nokia770_ads7846_props[] = {
PROPERTY_ENTRY_U16("ti,x-plate-ohms", 180),
PROPERTY_ENTRY_U16("ti,debounce-tol", 3),
PROPERTY_ENTRY_U16("ti,debounce-rep", 1),
PROPERTY_ENTRY_GPIO("pendown-gpios", &nokia770_gpiochip1_node,
ADS7846_PENDOWN_GPIO, GPIO_ACTIVE_LOW),
PROPERTY_ENTRY_GPIO("pendown-gpios", &omap16xx_gpio1_swnode, 15, GPIO_ACTIVE_LOW),
{ }
};
@@ -225,9 +203,9 @@ static inline void nokia770_mmc_init(void)
#if IS_ENABLED(CONFIG_I2C_CBUS_GPIO)
static const struct software_node_ref_args nokia770_cbus_gpio_refs[] = {
SOFTWARE_NODE_REFERENCE(&nokia770_mpuio_gpiochip_node, 9, 0),
SOFTWARE_NODE_REFERENCE(&nokia770_mpuio_gpiochip_node, 10, 0),
SOFTWARE_NODE_REFERENCE(&nokia770_mpuio_gpiochip_node, 11, 0),
SOFTWARE_NODE_REFERENCE(&omap16xx_mpu_gpio_swnode, 9, 0),
SOFTWARE_NODE_REFERENCE(&omap16xx_mpu_gpio_swnode, 10, 0),
SOFTWARE_NODE_REFERENCE(&omap16xx_mpu_gpio_swnode, 11, 0),
};
static const struct property_entry nokia770_cbus_props[] = {
@@ -318,7 +296,6 @@ static void __init omap_nokia770_init(void)
/* Unmask SleepX signal */
omap_writew((omap_readw(0xfffb5004) & ~2), 0xfffb5004);
software_node_register_node_group(nokia770_gpiochip_nodes);
platform_add_devices(nokia770_devices, ARRAY_SIZE(nokia770_devices));
gpiod_add_lookup_table(&nokia770_irq_gpio_table);
+3
View File
@@ -35,6 +35,9 @@
#include "soc.h"
#include "i2c.h"
extern const struct software_node omap16xx_mpu_gpio_swnode;
extern const struct software_node omap16xx_gpio1_swnode;
#ifdef CONFIG_OMAP_SERIAL_WAKE
int omap_serial_wakeup_init(void);
#else
+40 -40
View File
@@ -9,6 +9,7 @@
*/
#include <linux/platform_data/gpio-omap.h>
#include <linux/property.h>
#include <linux/soc/ti/omap1-io.h>
#include "hardware.h"
@@ -55,14 +56,16 @@ static struct omap_gpio_platform_data omap16xx_mpu_gpio_config = {
.regs = &omap16xx_mpuio_regs,
};
static struct platform_device omap16xx_mpu_gpio = {
const struct software_node omap16xx_mpu_gpio_swnode = { };
static const struct platform_device_info omap16xx_mpu_gpio = {
.name = "omap_gpio",
.id = 0,
.dev = {
.platform_data = &omap16xx_mpu_gpio_config,
},
.num_resources = ARRAY_SIZE(omap16xx_mpu_gpio_resources),
.resource = omap16xx_mpu_gpio_resources,
.data = &omap16xx_mpu_gpio_config,
.size_data = sizeof(omap16xx_mpu_gpio_config),
.num_res = ARRAY_SIZE(omap16xx_mpu_gpio_resources),
.res = omap16xx_mpu_gpio_resources,
.swnode = &omap16xx_mpu_gpio_swnode,
};
/* gpio1 */
@@ -99,14 +102,16 @@ static struct omap_gpio_platform_data omap16xx_gpio1_config = {
.regs = &omap16xx_gpio_regs,
};
static struct platform_device omap16xx_gpio1 = {
const struct software_node omap16xx_gpio1_swnode = { };
static const struct platform_device_info omap16xx_gpio1 = {
.name = "omap_gpio",
.id = 1,
.dev = {
.platform_data = &omap16xx_gpio1_config,
},
.num_resources = ARRAY_SIZE(omap16xx_gpio1_resources),
.resource = omap16xx_gpio1_resources,
.data = &omap16xx_gpio1_config,
.size_data = sizeof(omap16xx_gpio1_config),
.num_res = ARRAY_SIZE(omap16xx_gpio1_resources),
.res = omap16xx_gpio1_resources,
.swnode = &omap16xx_gpio1_swnode,
};
/* gpio2 */
@@ -127,14 +132,13 @@ static struct omap_gpio_platform_data omap16xx_gpio2_config = {
.regs = &omap16xx_gpio_regs,
};
static struct platform_device omap16xx_gpio2 = {
static const struct platform_device_info omap16xx_gpio2 = {
.name = "omap_gpio",
.id = 2,
.dev = {
.platform_data = &omap16xx_gpio2_config,
},
.num_resources = ARRAY_SIZE(omap16xx_gpio2_resources),
.resource = omap16xx_gpio2_resources,
.data = &omap16xx_gpio2_config,
.size_data = sizeof(omap16xx_gpio2_config),
.num_res = ARRAY_SIZE(omap16xx_gpio2_resources),
.res = omap16xx_gpio2_resources,
};
/* gpio3 */
@@ -155,14 +159,13 @@ static struct omap_gpio_platform_data omap16xx_gpio3_config = {
.regs = &omap16xx_gpio_regs,
};
static struct platform_device omap16xx_gpio3 = {
static const struct platform_device_info omap16xx_gpio3 = {
.name = "omap_gpio",
.id = 3,
.dev = {
.platform_data = &omap16xx_gpio3_config,
},
.num_resources = ARRAY_SIZE(omap16xx_gpio3_resources),
.resource = omap16xx_gpio3_resources,
.data = &omap16xx_gpio3_config,
.size_data = sizeof(omap16xx_gpio3_config),
.num_res = ARRAY_SIZE(omap16xx_gpio3_resources),
.res = omap16xx_gpio3_resources,
};
/* gpio4 */
@@ -183,17 +186,16 @@ static struct omap_gpio_platform_data omap16xx_gpio4_config = {
.regs = &omap16xx_gpio_regs,
};
static struct platform_device omap16xx_gpio4 = {
static const struct platform_device_info omap16xx_gpio4 = {
.name = "omap_gpio",
.id = 4,
.dev = {
.platform_data = &omap16xx_gpio4_config,
},
.num_resources = ARRAY_SIZE(omap16xx_gpio4_resources),
.resource = omap16xx_gpio4_resources,
.data = &omap16xx_gpio4_config,
.size_data = sizeof(omap16xx_gpio4_config),
.num_res = ARRAY_SIZE(omap16xx_gpio4_resources),
.res = omap16xx_gpio4_resources,
};
static struct platform_device *omap16xx_gpio_dev[] __initdata = {
static const struct platform_device_info *omap16xx_gpio_dev[] __initconst = {
&omap16xx_mpu_gpio,
&omap16xx_gpio1,
&omap16xx_gpio2,
@@ -210,9 +212,8 @@ static int __init omap16xx_gpio_init(void)
{
int i;
void __iomem *base;
struct resource *res;
struct platform_device *pdev;
struct omap_gpio_platform_data *pdata;
const struct resource *res;
const struct platform_device_info *pdevinfo;
if (!cpu_is_omap16xx())
return -EINVAL;
@@ -225,25 +226,24 @@ static int __init omap16xx_gpio_init(void)
ULPD_CAM_CLK_CTRL);
for (i = 0; i < ARRAY_SIZE(omap16xx_gpio_dev); i++) {
pdev = omap16xx_gpio_dev[i];
pdata = pdev->dev.platform_data;
pdevinfo = omap16xx_gpio_dev[i];
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
res = &pdevinfo->res[0];
if (unlikely(!res)) {
dev_err(&pdev->dev, "Invalid mem resource.\n");
pr_err("%s.%d: Invalid mem resource.\n", pdevinfo->name, pdevinfo->id);
return -ENODEV;
}
base = ioremap(res->start, resource_size(res));
if (unlikely(!base)) {
dev_err(&pdev->dev, "ioremap failed.\n");
pr_err("%s.%d: ioremap failed.\n", pdevinfo->name, pdevinfo->id);
return -ENOMEM;
}
__raw_writel(SYSCONFIG_WORD, base + OMAP1610_GPIO_SYSCONFIG);
iounmap(base);
platform_device_register(omap16xx_gpio_dev[i]);
platform_device_register_full(omap16xx_gpio_dev[i]);
}
return 0;
+2 -6
View File
@@ -367,17 +367,13 @@ static unsigned long __set_pages_state(struct snp_psc_desc *data, unsigned long
local_irq_save(flags);
if (sev_cfg.ghcbs_initialized)
ghcb = __sev_get_ghcb(&state);
else
ghcb = boot_ghcb;
ghcb = __sev_get_ghcb(&state);
/* Invoke the hypervisor to perform the page state changes */
if (!ghcb || vmgexit_psc(ghcb, data))
sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_PSC);
if (sev_cfg.ghcbs_initialized)
__sev_put_ghcb(&state);
__sev_put_ghcb(&state);
local_irq_restore(flags);
+3
View File
@@ -70,6 +70,9 @@ void svsm_pval_pages(struct snp_psc_desc *desc);
int svsm_perform_call_protocol(struct svsm_call *call);
bool snp_svsm_vtpm_probe(void);
noinstr void kernel_exc_vmm_communication(struct pt_regs *regs, unsigned long error_code);
noinstr void user_exc_vmm_communication(struct pt_regs *regs, unsigned long error_code);
static inline u64 sev_es_rd_ghcb_msr(void)
{
return native_rdmsrq(MSR_AMD64_SEV_ES_GHCB);
+3 -1
View File
@@ -121,8 +121,10 @@ noinstr struct ghcb *__sev_get_ghcb(struct ghcb_state *state)
WARN_ON(!irqs_disabled());
if (!sev_cfg.ghcbs_initialized)
if (!sev_cfg.ghcbs_initialized) {
state->ghcb = NULL;
return boot_ghcb;
}
data = this_cpu_read(runtime_data);
ghcb = &data->ghcb_page;
+2 -8
View File
@@ -74,20 +74,14 @@ int svsm_perform_call_protocol(struct svsm_call *call)
flags = native_local_irq_save();
if (sev_cfg.ghcbs_initialized)
ghcb = __sev_get_ghcb(&state);
else if (boot_ghcb)
ghcb = boot_ghcb;
else
ghcb = NULL;
ghcb = __sev_get_ghcb(&state);
do {
ret = ghcb ? svsm_perform_ghcb_protocol(ghcb, call)
: __pi_svsm_perform_msr_protocol(call);
} while (ret == -EAGAIN);
if (sev_cfg.ghcbs_initialized)
__sev_put_ghcb(&state);
__sev_put_ghcb(&state);
native_local_irq_restore(flags);
+10 -2
View File
@@ -954,7 +954,7 @@ static __always_inline bool vc_is_db(unsigned long error_code)
* Runtime #VC exception handler when raised from kernel mode. Runs in NMI mode
* and will panic when an error happens.
*/
DEFINE_IDTENTRY_VC_KERNEL(exc_vmm_communication)
noinstr void kernel_exc_vmm_communication(struct pt_regs *regs, unsigned long error_code)
{
irqentry_state_t irq_state;
@@ -1006,7 +1006,7 @@ DEFINE_IDTENTRY_VC_KERNEL(exc_vmm_communication)
* Runtime #VC exception handler when raised from user mode. Runs in IRQ mode
* and will kill the current task with SIGBUS when an error happens.
*/
DEFINE_IDTENTRY_VC_USER(exc_vmm_communication)
noinstr void user_exc_vmm_communication(struct pt_regs *regs, unsigned long error_code)
{
/*
* Handle #DB before calling into !noinstr code to avoid recursive #DB.
@@ -1032,6 +1032,14 @@ DEFINE_IDTENTRY_VC_USER(exc_vmm_communication)
irqentry_exit_to_user_mode(regs);
}
DEFINE_IDTENTRY_RAW_ERRORCODE(exc_vmm_communication)
{
if (user_mode(regs))
return user_exc_vmm_communication(regs, error_code);
else
return kernel_exc_vmm_communication(regs, error_code);
}
bool __init handle_vc_boot_ghcb(struct pt_regs *regs)
{
unsigned long exit_code = regs->orig_ax;
+2 -2
View File
@@ -492,7 +492,7 @@ SYM_CODE_START(\asmsym)
movq %rsp, %rdi /* pt_regs pointer */
call kernel_\cfunc
call \cfunc
/*
* No need to switch back to the IST stack. The current stack is either
@@ -503,7 +503,7 @@ SYM_CODE_START(\asmsym)
/* Switch to the regular task stack */
.Lfrom_usermode_switch_stack_\@:
idtentry_body user_\cfunc, has_error_code=1
idtentry_body \cfunc, has_error_code=1
_ASM_NOKPROBE(\asmsym)
SYM_CODE_END(\asmsym)
-10
View File
@@ -177,16 +177,6 @@ static noinstr void fred_extint(struct pt_regs *regs)
}
}
#ifdef CONFIG_AMD_MEM_ENCRYPT
noinstr void exc_vmm_communication(struct pt_regs *regs, unsigned long error_code)
{
if (user_mode(regs))
return user_exc_vmm_communication(regs, error_code);
else
return kernel_exc_vmm_communication(regs, error_code);
}
#endif
static noinstr void fred_hwexc(struct pt_regs *regs, unsigned long error_code)
{
/* Optimize for #PF. That's the only exception which matters performance wise */
+3 -1
View File
@@ -153,7 +153,7 @@ struct arch_va_list {
struct sysv_va_list args;
};
extern void *__warn_args(struct arch_va_list *args, struct pt_regs *regs);
#endif /* __ASSEMBLER__ */
static __always_inline __printf(1, 2) void __WARN_validate_printf(const char *fmt, ...) { }
#define __WARN_bug_entry(flags, format) ({ \
struct bug_entry *bug; \
@@ -172,6 +172,7 @@ extern void *__warn_args(struct arch_va_list *args, struct pt_regs *regs);
#define __WARN_print_arg(flags, format, arg...) \
do { \
int __flags = (flags) | BUGFLAG_WARNING | BUGFLAG_ARGS ; \
__WARN_validate_printf(format, ## arg); \
static_call_mod(WARN_trap)(__WARN_bug_entry(__flags, format), ## arg); \
asm (""); /* inhibit tail-call optimization */ \
} while (0)
@@ -187,6 +188,7 @@ do { \
} \
__ret_warn_on; \
})
#endif /* __ASSEMBLER__ */
#endif /* HAVE_ARCH_BUG_FORMAT_ARGS */
+1
View File
@@ -573,4 +573,5 @@
#define X86_BUG_ITS_NATIVE_ONLY X86_BUG( 1*32+ 8) /* "its_native_only" CPU is affected by ITS, VMX is not affected */
#define X86_BUG_TSA X86_BUG( 1*32+ 9) /* "tsa" CPU is affected by Transient Scheduler Attacks */
#define X86_BUG_VMSCAPE X86_BUG( 1*32+10) /* "vmscape" CPU is affected by VMSCAPE attacks from guests */
#define X86_BUG_SEAMRET_INVD_VMCS X86_BUG( 1*32+11) /* "seamret_invd_vmcs" SEAMRET from P-SEAMLDR clears the current VMCS */
#endif /* _ASM_X86_CPUFEATURES_H */
-11
View File
@@ -1,11 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _ASM_X86_DEVICE_H
#define _ASM_X86_DEVICE_H
struct dev_archdata {
};
struct pdev_archdata {
};
#endif /* _ASM_X86_DEVICE_H */
+3 -26
View File
@@ -340,17 +340,14 @@ static __always_inline void __##func(struct pt_regs *regs)
__visible void noist_##func(struct pt_regs *regs)
/**
* DECLARE_IDTENTRY_VC - Declare functions for the VC entry point
* DECLARE_IDTENTRY_VC - Declare a function for the VC entry point
* @vector: Vector number (ignored for C)
* @func: Function name of the entry point
*
* Maps to DECLARE_IDTENTRY_RAW_ERRORCODE, but declares also the
* safe_stack C handler.
* Maps to DECLARE_IDTENTRY_RAW_ERRORCODE.
*/
#define DECLARE_IDTENTRY_VC(vector, func) \
DECLARE_IDTENTRY_RAW_ERRORCODE(vector, func); \
__visible noinstr void kernel_##func(struct pt_regs *regs, unsigned long error_code); \
__visible noinstr void user_##func(struct pt_regs *regs, unsigned long error_code)
DECLARE_IDTENTRY_RAW_ERRORCODE(vector, func);
/**
* DEFINE_IDTENTRY_IST - Emit code for IST entry points
@@ -391,26 +388,6 @@ static __always_inline void __##func(struct pt_regs *regs)
#define DEFINE_IDTENTRY_DF(func) \
DEFINE_IDTENTRY_RAW_ERRORCODE(func)
/**
* DEFINE_IDTENTRY_VC_KERNEL - Emit code for VMM communication handler
* when raised from kernel mode
* @func: Function name of the entry point
*
* Maps to DEFINE_IDTENTRY_RAW_ERRORCODE
*/
#define DEFINE_IDTENTRY_VC_KERNEL(func) \
DEFINE_IDTENTRY_RAW_ERRORCODE(kernel_##func)
/**
* DEFINE_IDTENTRY_VC_USER - Emit code for VMM communication handler
* when raised from user mode
* @func: Function name of the entry point
*
* Maps to DEFINE_IDTENTRY_RAW_ERRORCODE
*/
#define DEFINE_IDTENTRY_VC_USER(func) \
DEFINE_IDTENTRY_RAW_ERRORCODE(user_##func)
#else /* CONFIG_X86_64 */
/**
+38
View File
@@ -0,0 +1,38 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _ASM_X86_SEAMLDR_H
#define _ASM_X86_SEAMLDR_H
#include <linux/types.h>
/*
* This is the "SEAMLDR_INFO" data structure defined in the
* "SEAM Loader (SEAMLDR) Interface Specification".
*
* Must be aligned to a 256-byte boundary.
*/
struct seamldr_info {
u32 version;
u32 attributes;
u32 vendor_id;
u32 build_date;
u16 build_num;
u16 minor_version;
u16 major_version;
u16 update_version;
u32 acm_x2apicid;
u32 num_remaining_updates;
u8 seam_info[128];
u8 seam_ready;
u8 seam_debug;
u8 p_seam_ready;
u8 reserved[93];
} __packed __aligned(256);
static_assert(sizeof(struct seamldr_info) == 256);
int seamldr_get_info(struct seamldr_info *seamldr_info);
int seamldr_install_module(const u8 *data, u32 data_len);
void seamldr_lock_module_update(void);
void seamldr_unlock_module_update(void);
#endif /* _ASM_X86_SEAMLDR_H */
+2 -2
View File
@@ -661,7 +661,7 @@ static inline void snp_leak_pages(u64 pfn, unsigned int pages)
{
__snp_leak_pages(pfn, pages, true);
}
void snp_prepare(void);
int snp_prepare(void);
void snp_shutdown(void);
#else
static inline bool snp_probe_rmptable_info(void) { return false; }
@@ -679,7 +679,7 @@ static inline void __snp_leak_pages(u64 pfn, unsigned int npages, bool dump_rmp)
static inline void snp_leak_pages(u64 pfn, unsigned int npages) {}
static inline void kdump_sev_callback(void) { }
static inline void snp_fixup_e820_tables(void) {}
static inline void snp_prepare(void) {}
static inline int snp_prepare(void) { return -ENODEV; }
static inline void snp_shutdown(void) {}
#endif
+1
View File
@@ -4,6 +4,7 @@
#include <linux/bits.h>
#include <linux/types.h>
#include <asm/shared/tdx_errno.h>
#define TDX_HYPERCALL_STANDARD 0
@@ -1,8 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* architectural status code for SEAMCALL */
#ifndef __KVM_X86_TDX_ERRNO_H
#define __KVM_X86_TDX_ERRNO_H
#ifndef _ASM_X86_SHARED_TDX_ERRNO_H
#define _ASM_X86_SHARED_TDX_ERRNO_H
#define TDX_SEAMCALL_STATUS_MASK 0xFFFFFFFF00000000ULL
@@ -14,6 +13,7 @@
#define TDX_NON_RECOVERABLE_TD_NON_ACCESSIBLE 0x6000000500000000ULL
#define TDX_NON_RECOVERABLE_TD_WRONG_APIC_MODE 0x6000000700000000ULL
#define TDX_INTERRUPTED_RESUMABLE 0x8000000300000000ULL
#define TDX_SYS_BUSY 0x8000020200000000ULL
#define TDX_OPERAND_INVALID 0xC000010000000000ULL
#define TDX_OPERAND_BUSY 0x8000020000000000ULL
#define TDX_PREVIOUS_TLB_EPOCH_BUSY 0x8000020100000000ULL
@@ -37,4 +37,4 @@
#define TDX_OPERAND_ID_SEPT 0x92
#define TDX_OPERAND_ID_TD_EPOCH 0xa9
#endif /* __KVM_X86_TDX_ERRNO_H */
#endif /* _ASM_X86_SHARED_TDX_ERRNO_H */
+19 -53
View File
@@ -32,12 +32,22 @@
#define TDX_SUCCESS 0ULL
#define TDX_RND_NO_ENTROPY 0x8000020300000000ULL
/* Bit definitions of TDX_FEATURES0 metadata field */
#define TDX_FEATURES0_TD_PRESERVING BIT_ULL(1)
#define TDX_FEATURES0_NO_RBP_MOD BIT_ULL(18)
#ifndef __ASSEMBLER__
#include <uapi/asm/mce.h>
#include <asm/tdx_global_metadata.h>
#include <linux/pgtable.h>
/*
* TDX module and P-SEAMLDR version convention: "major.minor.update"
* (e.g., "1.5.08") with zero-padded two-digit update field.
*/
#define TDX_VERSION_FMT "%u.%u.%02u"
/*
* Used by the #VE exception handler to gather the #VE exception
* info from the TDX module. This is a software only structure
@@ -97,57 +107,16 @@ static inline long tdx_kvm_hypercall(unsigned int nr, unsigned long p1,
#endif /* CONFIG_INTEL_TDX_GUEST && CONFIG_KVM_GUEST */
#ifdef CONFIG_INTEL_TDX_HOST
u64 __seamcall(u64 fn, struct tdx_module_args *args);
u64 __seamcall_ret(u64 fn, struct tdx_module_args *args);
u64 __seamcall_saved_ret(u64 fn, struct tdx_module_args *args);
void tdx_init(void);
#include <linux/preempt.h>
#include <asm/archrandom.h>
#include <asm/processor.h>
typedef u64 (*sc_func_t)(u64 fn, struct tdx_module_args *args);
static __always_inline u64 __seamcall_dirty_cache(sc_func_t func, u64 fn,
struct tdx_module_args *args)
{
lockdep_assert_preemption_disabled();
/*
* SEAMCALLs are made to the TDX module and can generate dirty
* cachelines of TDX private memory. Mark cache state incoherent
* so that the cache can be flushed during kexec.
*
* This needs to be done before actually making the SEAMCALL,
* because kexec-ing CPU could send NMI to stop remote CPUs,
* in which case even disabling IRQ won't help here.
*/
this_cpu_write(cache_state_incoherent, true);
return func(fn, args);
}
static __always_inline u64 sc_retry(sc_func_t func, u64 fn,
struct tdx_module_args *args)
{
int retry = RDRAND_RETRY_LOOPS;
u64 ret;
do {
preempt_disable();
ret = __seamcall_dirty_cache(func, fn, args);
preempt_enable();
} while (ret == TDX_RND_NO_ENTROPY && --retry);
return ret;
}
#define seamcall(_fn, _args) sc_retry(__seamcall, (_fn), (_args))
#define seamcall_ret(_fn, _args) sc_retry(__seamcall_ret, (_fn), (_args))
#define seamcall_saved_ret(_fn, _args) sc_retry(__seamcall_saved_ret, (_fn), (_args))
int tdx_cpu_enable(void);
const char *tdx_dump_mce_info(struct mce *m);
const struct tdx_sys_info *tdx_get_sysinfo(void);
static inline bool tdx_supports_runtime_update(const struct tdx_sys_info *sysinfo)
{
return sysinfo->features.tdx_features0 & TDX_FEATURES0_TD_PRESERVING;
}
int tdx_guest_keyid_alloc(void);
u32 tdx_get_nr_guest_keyids(void);
void tdx_guest_keyid_free(unsigned int keyid);
@@ -193,6 +162,8 @@ static inline int pg_level_to_tdx_sept_level(enum pg_level level)
return level - 1;
}
void tdx_sys_disable(void);
u64 tdh_vp_enter(struct tdx_vp *vp, struct tdx_module_args *args);
u64 tdh_mng_addcx(struct tdx_td *td, struct page *tdcs_page);
u64 tdh_mem_page_add(struct tdx_td *td, u64 gpa, struct page *page, struct page *source, u64 *ext_err1, u64 *ext_err2);
@@ -224,13 +195,8 @@ static inline void tdx_init(void) { }
static inline u32 tdx_get_nr_guest_keyids(void) { return 0; }
static inline const char *tdx_dump_mce_info(struct mce *m) { return NULL; }
static inline const struct tdx_sys_info *tdx_get_sysinfo(void) { return NULL; }
static inline void tdx_sys_disable(void) { }
#endif /* CONFIG_INTEL_TDX_HOST */
#ifdef CONFIG_KEXEC_CORE
void tdx_cpu_flush_cache_for_kexec(void);
#else
static inline void tdx_cpu_flush_cache_for_kexec(void) { }
#endif
#endif /* !__ASSEMBLER__ */
#endif /* _ASM_X86_TDX_H */
@@ -40,6 +40,10 @@ struct tdx_sys_info_td_conf {
u64 cpuid_config_values[128][2];
};
struct tdx_sys_info_handoff {
u16 module_hv;
};
struct tdx_sys_info {
struct tdx_sys_info_version version;
struct tdx_sys_info_features features;
+1
View File
@@ -147,6 +147,7 @@ struct vmcs {
#define VMX_BASIC_INOUT BIT_ULL(54)
#define VMX_BASIC_TRUE_CTLS BIT_ULL(55)
#define VMX_BASIC_NO_HW_ERROR_CODE_CC BIT_ULL(56)
#define VMX_BASIC_NO_SEAMRET_INVD_VMCS BIT_ULL(60)
static inline u32 vmx_basic_vmcs_revision_id(u64 vmx_basic)
{
+2
View File
@@ -38,6 +38,7 @@
#include <linux/kdebug.h>
#include <asm/cpu.h>
#include <asm/reboot.h>
#include <asm/tdx.h>
#include <asm/intel_pt.h>
#include <asm/crash.h>
#include <asm/cmdline.h>
@@ -112,6 +113,7 @@ void native_machine_crash_shutdown(struct pt_regs *regs)
crash_smp_send_stop();
tdx_sys_disable();
x86_virt_emergency_disable_virtualization_cpu();
/*
-16
View File
@@ -347,22 +347,6 @@ int machine_kexec_prepare(struct kimage *image)
unsigned long reloc_end = (unsigned long)__relocate_kernel_end;
int result;
/*
* Some early TDX-capable platforms have an erratum. A kernel
* partial write (a write transaction of less than cacheline
* lands at memory controller) to TDX private memory poisons that
* memory, and a subsequent read triggers a machine check.
*
* On those platforms the old kernel must reset TDX private
* memory before jumping to the new kernel otherwise the new
* kernel may see unexpected machine check. For simplicity
* just fail kexec/kdump on those platforms.
*/
if (boot_cpu_has_bug(X86_BUG_TDX_PW_MCE)) {
pr_info_once("Not allowed on platform with tdx_pw_mce bug\n");
return -EOPNOTSUPP;
}
/* Setup the identity mapped 64bit page table */
result = init_pgtable(image, __pa(control_page));
if (result)
-10
View File
@@ -440,16 +440,6 @@ void tdx_disable_virtualization_cpu(void)
tdx_flush_vp(&arg);
}
local_irq_restore(flags);
/*
* Flush cache now if kexec is possible: this is necessary to avoid
* having dirty private memory cachelines when the new kernel boots,
* but WBINVD is a relatively expensive operation and doing it during
* kexec can exacerbate races in native_stop_other_cpus(). Do it
* now, since this is a safe moment and there is going to be no more
* TDX activity on this CPU from this point on.
*/
tdx_cpu_flush_cache_for_kexec();
}
#define TDX_SEAMCALL_RETRIES 10000
-1
View File
@@ -3,7 +3,6 @@
#define __KVM_X86_VMX_TDX_H
#include "tdx_arch.h"
#include "tdx_errno.h"
#ifdef CONFIG_KVM_INTEL_TDX
#include "common.h"
+5
View File
@@ -169,6 +169,11 @@ is_prefetch(struct pt_regs *regs, unsigned long error_code, unsigned long addr)
}
DEFINE_SPINLOCK(pgd_lock);
/*
* List of all process' PGD pages (excluding init_mm and some other special
* system-wide PGDs). Mainly used to keep the kernel portion of the address
* space in sync across mms.
*/
LIST_HEAD(pgd_list);
#ifdef CONFIG_X86_32
+26 -14
View File
@@ -1014,7 +1014,7 @@ static void __meminit free_pagetable(struct page *page, int order)
#ifdef CONFIG_HAVE_BOOTMEM_INFO_NODE
enum bootmem_type type = bootmem_type(page);
if (type == SECTION_INFO || type == MIX_SECTION_INFO) {
if (type == MIX_SECTION_INFO) {
while (nr_pages--)
put_page_bootmem(page++);
} else {
@@ -1028,13 +1028,24 @@ static void __meminit free_pagetable(struct page *page, int order)
}
}
static void __meminit free_hugepage_table(struct page *page,
static void __meminit free_vmemmap_pages(struct page *page, unsigned int order,
struct vmem_altmap *altmap)
{
if (altmap)
vmem_altmap_free(altmap, PMD_SIZE / PAGE_SIZE);
else
free_pagetable(page, get_order(PMD_SIZE));
unsigned long nr_pages = 1u << order;
if (altmap) {
vmem_altmap_free(altmap, nr_pages);
} else if (PageReserved(page)) {
if (IS_ENABLED(CONFIG_HAVE_BOOTMEM_INFO_NODE) &&
bootmem_type(page) == SECTION_INFO) {
while (nr_pages--)
put_page_bootmem(page++);
} else {
free_reserved_pages(page, nr_pages);
}
} else {
__free_pages(page, order);
}
}
static void __meminit free_pte_table(pte_t *pte_start, pmd_t *pmd)
@@ -1118,7 +1129,8 @@ remove_pte_table(pte_t *pte_start, unsigned long addr, unsigned long end,
return;
if (!direct)
free_pagetable(pte_page(*pte), 0);
/* We never populate base pages from the altmap. */
free_vmemmap_pages(pte_page(*pte), 0, NULL);
spin_lock(&init_mm.page_table_lock);
pte_clear(&init_mm, addr, pte);
@@ -1153,19 +1165,19 @@ remove_pmd_table(pmd_t *pmd_start, unsigned long addr, unsigned long end,
if (IS_ALIGNED(addr, PMD_SIZE) &&
IS_ALIGNED(next, PMD_SIZE)) {
if (!direct)
free_hugepage_table(pmd_page(*pmd),
altmap);
free_vmemmap_pages(pmd_page(*pmd),
PMD_ORDER, altmap);
spin_lock(&init_mm.page_table_lock);
pmd_clear(pmd);
spin_unlock(&init_mm.page_table_lock);
pages++;
} else if (vmemmap_pmd_is_unused(addr, next)) {
free_hugepage_table(pmd_page(*pmd),
altmap);
spin_lock(&init_mm.page_table_lock);
pmd_clear(pmd);
spin_unlock(&init_mm.page_table_lock);
free_vmemmap_pages(pmd_page(*pmd), PMD_ORDER,
altmap);
spin_lock(&init_mm.page_table_lock);
pmd_clear(pmd);
spin_unlock(&init_mm.page_table_lock);
}
continue;
}
-11
View File
@@ -99,17 +99,6 @@ static void pgd_dtor(pgd_t *pgd)
spin_unlock(&pgd_lock);
}
/*
* List of all pgd's needed for non-PAE so it can invalidate entries
* in both cached and uncached pgd's; not needed for PAE since the
* kernel pmd is shared. If PAE were not to share the pmd a similar
* tactic would be needed. This is essentially codepath-based locking
* against pageattr.c; it is the unique case in which a valid change
* of kernel pagetables can't be lazily synchronized by vmalloc faults.
* vmalloc faults work because attached pagetables are never freed.
* -- nyc
*/
#ifdef CONFIG_X86_PAE
/*
* In PAE mode, we need to do a cr3 reload (=tlb flush) when
+16 -2
View File
@@ -511,8 +511,9 @@ static void clear_hsave_pa(void *arg)
wrmsrq(MSR_VM_HSAVE_PA, 0);
}
void snp_prepare(void)
int snp_prepare(void)
{
int ret;
u64 val;
/*
@@ -521,12 +522,20 @@ void snp_prepare(void)
*/
rdmsrq(MSR_AMD64_SYSCFG, val);
if (val & MSR_AMD64_SYSCFG_SNP_EN)
return;
return 0;
clear_rmp();
cpus_read_lock();
if (!cpumask_equal(cpu_online_mask, cpu_present_mask)) {
ret = -EOPNOTSUPP;
pr_warn("SNP init failed: not all CPUs online. (%*pbl online <-> %*pbl present masks).\n",
cpumask_pr_args(cpu_online_mask),
cpumask_pr_args(cpu_present_mask));
goto unlock;
}
/*
* MtrrFixDramModEn is not shared between threads on a core,
* therefore it must be set on all CPUs prior to enabling SNP.
@@ -537,7 +546,12 @@ void snp_prepare(void)
/* SNP_INIT requires MSR_VM_HSAVE_PA to be cleared on all CPUs. */
on_each_cpu(clear_hsave_pa, NULL, 1);
ret = 0;
unlock:
cpus_read_unlock();
return ret;
}
EXPORT_SYMBOL_FOR_MODULES(snp_prepare, "ccp");
+1 -1
View File
@@ -1,2 +1,2 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-y += seamcall.o tdx.o
obj-y += seamcall.o seamldr.o tdx.o
+109
View File
@@ -0,0 +1,109 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* SEAMCALL utilities for TDX host-side operations.
*
* Provides convenient wrappers around SEAMCALL assembly with retry logic,
* error reporting and cache coherency tracking.
*
* Copyright (C) 2021-2023 Intel Corporation
*/
#ifndef _X86_VIRT_SEAMCALL_INTERNAL_H
#define _X86_VIRT_SEAMCALL_INTERNAL_H
#include <linux/printk.h>
#include <linux/types.h>
#include <asm/archrandom.h>
#include <asm/processor.h>
#include <asm/tdx.h>
u64 __seamcall(u64 fn, struct tdx_module_args *args);
u64 __seamcall_ret(u64 fn, struct tdx_module_args *args);
u64 __seamcall_saved_ret(u64 fn, struct tdx_module_args *args);
typedef u64 (*sc_func_t)(u64 fn, struct tdx_module_args *args);
static __always_inline u64 __seamcall_dirty_cache(sc_func_t func, u64 fn,
struct tdx_module_args *args)
{
lockdep_assert_preemption_disabled();
/*
* SEAMCALLs are made to the TDX module and can generate dirty
* cachelines of TDX private memory. Mark cache state incoherent
* so that the cache can be flushed during kexec.
*
* This needs to be done before actually making the SEAMCALL,
* because kexec-ing CPU could send NMI to stop remote CPUs,
* in which case even disabling IRQ won't help here.
*/
this_cpu_write(cache_state_incoherent, true);
return func(fn, args);
}
static __always_inline u64 sc_retry(sc_func_t func, u64 fn,
struct tdx_module_args *args)
{
int retry = RDRAND_RETRY_LOOPS;
u64 ret;
do {
preempt_disable();
ret = __seamcall_dirty_cache(func, fn, args);
preempt_enable();
} while (ret == TDX_RND_NO_ENTROPY && --retry);
return ret;
}
#define seamcall(_fn, _args) sc_retry(__seamcall, (_fn), (_args))
#define seamcall_ret(_fn, _args) sc_retry(__seamcall_ret, (_fn), (_args))
#define seamcall_saved_ret(_fn, _args) sc_retry(__seamcall_saved_ret, (_fn), (_args))
typedef void (*sc_err_func_t)(u64 fn, u64 err, struct tdx_module_args *args);
static inline void seamcall_err(u64 fn, u64 err, struct tdx_module_args *args)
{
pr_err("SEAMCALL (0x%016llx) failed: 0x%016llx\n", fn, err);
}
static inline void seamcall_err_ret(u64 fn, u64 err,
struct tdx_module_args *args)
{
seamcall_err(fn, err, args);
pr_err("RCX 0x%016llx RDX 0x%016llx R08 0x%016llx\n",
args->rcx, args->rdx, args->r8);
pr_err("R09 0x%016llx R10 0x%016llx R11 0x%016llx\n",
args->r9, args->r10, args->r11);
}
static __always_inline int sc_retry_prerr(sc_func_t func,
sc_err_func_t err_func,
u64 fn, struct tdx_module_args *args)
{
u64 sret = sc_retry(func, fn, args);
if (sret == TDX_SUCCESS)
return 0;
if (sret == TDX_SEAMCALL_VMFAILINVALID)
return -ENODEV;
if (sret == TDX_SEAMCALL_GP)
return -EOPNOTSUPP;
if (sret == TDX_SEAMCALL_UD)
return -EACCES;
err_func(fn, sret, args);
return -EIO;
}
#define seamcall_prerr(__fn, __args) \
sc_retry_prerr(__seamcall, seamcall_err, (__fn), (__args))
#define seamcall_prerr_ret(__fn, __args) \
sc_retry_prerr(__seamcall_ret, seamcall_err_ret, (__fn), (__args))
#endif /* _X86_VIRT_SEAMCALL_INTERNAL_H */
+368
View File
@@ -0,0 +1,368 @@
// SPDX-License-Identifier: GPL-2.0
/*
* P-SEAMLDR support for TDX module management features like runtime updates
*
* Copyright (C) 2025 Intel Corporation
*/
#define pr_fmt(fmt) "seamldr: " fmt
#include <linux/bug.h>
#include <linux/mm.h>
#include <linux/nmi.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/stop_machine.h>
#include <asm/cpufeature.h>
#include <asm/cpufeatures.h>
#include <asm/seamldr.h>
#include "seamcall_internal.h"
#include "tdx.h"
/* P-SEAMLDR SEAMCALL leaf function */
#define P_SEAMLDR_INFO 0x8000000000000000
#define P_SEAMLDR_INSTALL 0x8000000000000001
#define SEAMLDR_MAX_NR_MODULE_PAGES 496
#define SEAMLDR_MAX_NR_SIG_PAGES 1
/*
* The seamldr_params "scenario" field specifies the operation mode:
* 0: Install TDX module from scratch (not used by kernel)
* 1: Update existing TDX module to a compatible version
*/
#define SEAMLDR_SCENARIO_UPDATE 1
/*
* This is the "SEAMLDR_PARAMS" data structure defined in the
* "SEAM Loader (SEAMLDR) Interface Specification".
*
* It is the in-memory ABI that the kernel passes to the P-SEAMLDR
* to update the TDX module. It breaks the TDX module image up in
* page-size pieces.
*/
struct seamldr_params {
u32 version;
u32 scenario;
u64 sigstruct_pages_pa_list[SEAMLDR_MAX_NR_SIG_PAGES];
u8 reserved[104];
u64 module_nr_pages;
u64 module_pages_pa_list[SEAMLDR_MAX_NR_MODULE_PAGES];
} __packed;
static_assert(sizeof(struct seamldr_params) == 4096);
/*
* Serialize P-SEAMLDR calls since the hardware only allows a single CPU to
* interact with P-SEAMLDR simultaneously. Use raw version as the calls can
* be made with interrupts disabled, where plain spinlocks are prohibited in
* PREEMPT_RT kernels as they become sleeping locks.
*/
static DEFINE_RAW_SPINLOCK(seamldr_lock);
static int seamldr_call(u64 fn, struct tdx_module_args *args)
{
/*
* With this bug, P-SEAMLDR calls corrupt the VMCS
* pointer and must be avoided. This path should be
* unreachable since sysfs hides the ABIs.
*/
if (boot_cpu_has_bug(X86_BUG_SEAMRET_INVD_VMCS)) {
WARN_ON(1);
return -EINVAL;
}
guard(raw_spinlock)(&seamldr_lock);
return seamcall_prerr(fn, args);
}
int seamldr_get_info(struct seamldr_info *seamldr_info)
{
struct tdx_module_args args = {};
/*
* Use slow_virt_to_phys() since @seamldr_info may be allocated on
* the stack.
*/
args.rcx = slow_virt_to_phys(seamldr_info);
return seamldr_call(P_SEAMLDR_INFO, &args);
}
EXPORT_SYMBOL_FOR_MODULES(seamldr_get_info, "tdx-host");
/* Call into P-SEAMLDR to install a TDX module update */
static int seamldr_install(const struct seamldr_params *params)
{
struct tdx_module_args args = {};
args.rcx = __pa(params);
return seamldr_call(P_SEAMLDR_INSTALL, &args);
}
#define TDX_IMAGE_VERSION_2 0x200
/* First page of the on-disk module update image: */
struct tdx_image_header {
u16 version;
u16 checksum;
u8 signature[8];
u32 sigstruct_nr_pages;
u32 module_nr_pages;
u8 reserved[4076];
} __packed;
#define TDX_IMAGE_HEADER_SIZE sizeof(struct tdx_image_header)
static_assert(TDX_IMAGE_HEADER_SIZE == 4096);
/*
* Intel TDX module update ABI structure. aka. "TDX module blob".
* This is the on-disk format that fw_upload lands in a kernel
* buffer.
*
* @payload contains sigstruct pages followed by module pages.
*/
struct tdx_image {
struct tdx_image_header header;
u8 payload[];
};
/*
* Given a vmalloc() allocation, write all of the backing physical
* addresses to pa_list[]. Caller guarantees that the array is big
* enough.
*/
static void populate_pa_list(u64 *pa_list, const u8 *vmalloc_addr, u32 vmalloc_len_pages)
{
int i;
for (i = 0; i < vmalloc_len_pages; i++) {
unsigned long offset = i * PAGE_SIZE;
unsigned long pfn = vmalloc_to_pfn(&vmalloc_addr[offset]);
pa_list[i] = pfn << PAGE_SHIFT;
}
}
static void populate_seamldr_params(struct seamldr_params *params,
const u8 *sig, u32 sig_nr_pages,
const u8 *mod, u32 mod_nr_pages)
{
params->version = 0;
params->scenario = SEAMLDR_SCENARIO_UPDATE;
params->module_nr_pages = mod_nr_pages;
populate_pa_list(params->sigstruct_pages_pa_list, sig, sig_nr_pages);
populate_pa_list(params->module_pages_pa_list, mod, mod_nr_pages);
}
/*
* @image points to a vmalloc()'d 'struct tdx_image'. Transform
* it into @params which is the P-SEAMLDR ABI format.
*/
static int init_seamldr_params(struct seamldr_params *params,
const struct tdx_image *image,
u32 image_len)
{
const struct tdx_image_header *header = &image->header;
u32 sigstruct_len = header->sigstruct_nr_pages * PAGE_SIZE;
u32 module_len = header->module_nr_pages * PAGE_SIZE;
u8 *header_start = (u8 *)header;
u8 *header_end = header_start + TDX_IMAGE_HEADER_SIZE;
u8 *sigstruct_start = header_end;
u8 *sigstruct_end = sigstruct_start + sigstruct_len;
u8 *module_start = sigstruct_end;
/* Check the calculated payload size against the image size. */
if (TDX_IMAGE_HEADER_SIZE + sigstruct_len + module_len != image_len)
return -EINVAL;
/* Reject unsupported tdx_image ABI versions. */
if (header->version != TDX_IMAGE_VERSION_2)
return -EINVAL;
if (header->sigstruct_nr_pages > SEAMLDR_MAX_NR_SIG_PAGES ||
header->module_nr_pages > SEAMLDR_MAX_NR_MODULE_PAGES)
return -EINVAL;
if (memcmp(header->signature, "TDX-BLOB", sizeof(header->signature)))
return -EINVAL;
if (memchr_inv(header->reserved, 0, sizeof(header->reserved)))
return -EINVAL;
populate_seamldr_params(params, sigstruct_start, header->sigstruct_nr_pages,
module_start, header->module_nr_pages);
return 0;
}
/*
* During a TDX module update, all CPUs start from MODULE_UPDATE_START and
* progress to MODULE_UPDATE_DONE. Each state is associated with certain
* work. For some states, just one CPU needs to perform the work, while
* other CPUs just wait during those states.
*/
enum module_update_state {
MODULE_UPDATE_START,
MODULE_UPDATE_SHUTDOWN,
MODULE_UPDATE_CPU_INSTALL,
MODULE_UPDATE_CPU_INIT,
MODULE_UPDATE_RUN_UPDATE,
MODULE_UPDATE_DONE,
};
static struct update_ctrl {
enum module_update_state state;
int num_ack;
int num_failed;
/*
* Protect update_ctrl. Raw spinlock as it will be acquired from
* interrupt-disabled contexts.
*/
raw_spinlock_t lock;
} update_ctrl;
/* Called with ctrl->lock held or during initialization. */
static void __set_target_state(struct update_ctrl *ctrl,
enum module_update_state newstate)
{
/* Reset ack counter. */
ctrl->num_ack = 0;
ctrl->state = newstate;
}
/* Last one to ack a state moves to the next state. */
static void ack_state(struct update_ctrl *ctrl, int result)
{
raw_spin_lock(&ctrl->lock);
ctrl->num_failed += !!result;
ctrl->num_ack++;
if (ctrl->num_ack == num_online_cpus() && !ctrl->num_failed)
__set_target_state(ctrl, ctrl->state + 1);
raw_spin_unlock(&ctrl->lock);
}
static void init_state(struct update_ctrl *ctrl)
{
raw_spin_lock_init(&ctrl->lock);
__set_target_state(ctrl, MODULE_UPDATE_START + 1);
ctrl->num_failed = 0;
}
/*
* See multi_cpu_stop() from where this multi-cpu state-machine was
* adopted.
*/
static int do_seamldr_install_module(void *seamldr_params)
{
enum module_update_state curstate = MODULE_UPDATE_START;
enum module_update_state newstate;
bool is_lead_cpu = false;
int ret = 0;
/*
* Some steps must be run on exactly one CPU. Pick a "lead" CPU to
* execute those steps. Use CPU 0 because it is always online.
*/
if (smp_processor_id() == 0)
is_lead_cpu = true;
do {
newstate = READ_ONCE(update_ctrl.state);
if (curstate == newstate) {
cpu_relax();
continue;
}
curstate = newstate;
switch (curstate) {
case MODULE_UPDATE_SHUTDOWN:
if (is_lead_cpu)
ret = tdx_module_shutdown();
break;
case MODULE_UPDATE_CPU_INSTALL:
ret = seamldr_install(seamldr_params);
break;
case MODULE_UPDATE_CPU_INIT:
ret = tdx_cpu_enable();
break;
case MODULE_UPDATE_RUN_UPDATE:
if (is_lead_cpu)
ret = tdx_module_run_update();
break;
default:
break;
}
ack_state(&update_ctrl, ret);
} while (curstate != MODULE_UPDATE_DONE &&
!READ_ONCE(update_ctrl.num_failed));
return ret;
}
/**
* seamldr_install_module - Install a new TDX module.
* @data: Pointer to the TDX module image.
* @data_len: Size of the TDX module image.
*
* Returns 0 on success, negative error code on failure.
*/
int seamldr_install_module(const u8 *data, u32 data_len)
{
struct seamldr_params *params;
const struct tdx_image *image;
int ret;
/*
* init_seamldr_params() reads the header early.
* Ensure there is enough data to do at least that.
*/
if (data_len < TDX_IMAGE_HEADER_SIZE)
return -EINVAL;
image = (const struct tdx_image *)data;
params = kzalloc_obj(*params);
if (!params)
return -ENOMEM;
/* Populate 'params' from 'image'. */
ret = init_seamldr_params(params, image, data_len);
if (ret)
goto out;
/* Ensure a stable set of online CPUs for the update process. */
cpus_read_lock();
init_state(&update_ctrl);
ret = stop_machine_cpuslocked(do_seamldr_install_module, params,
cpu_online_mask);
cpus_read_unlock();
out:
kfree(params);
return ret;
}
EXPORT_SYMBOL_FOR_MODULES(seamldr_install_module, "tdx-host");
/*
* stop_machine() does not interrupt preemption-disabled regions.
* Simply disabling preempt prevents updates.
*/
void seamldr_lock_module_update(void)
{
preempt_disable();
}
EXPORT_SYMBOL_FOR_MODULES(seamldr_lock_module_update, "tdx-host");
void seamldr_unlock_module_update(void)
{
preempt_enable();
}
EXPORT_SYMBOL_FOR_MODULES(seamldr_unlock_module_update, "tdx-host");
+149 -72
View File
@@ -37,12 +37,23 @@
#include <asm/msr.h>
#include <asm/cpufeature.h>
#include <asm/tdx.h>
#include <asm/shared/tdx_errno.h>
#include <asm/cpu_device_id.h>
#include <asm/processor.h>
#include <asm/mce.h>
#include <asm/virt.h>
#include <asm/vmx.h>
#include "seamcall_internal.h"
#include "tdx.h"
struct tdx_module_state {
bool initialized;
bool sysinit_done;
int sysinit_ret;
};
static struct tdx_module_state tdx_module_state;
static u32 tdx_global_keyid __ro_after_init;
static u32 tdx_guest_keyid_start __ro_after_init;
static u32 tdx_nr_guest_keyids __ro_after_init;
@@ -56,53 +67,9 @@ static struct tdmr_info_list tdx_tdmr_list;
/* All TDX-usable memory regions. Protected by mem_hotplug_lock. */
static LIST_HEAD(tdx_memlist);
static struct tdx_sys_info tdx_sysinfo __ro_after_init;
static bool tdx_module_initialized __ro_after_init;
static struct tdx_sys_info tdx_sysinfo;
typedef void (*sc_err_func_t)(u64 fn, u64 err, struct tdx_module_args *args);
static inline void seamcall_err(u64 fn, u64 err, struct tdx_module_args *args)
{
pr_err("SEAMCALL (0x%016llx) failed: 0x%016llx\n", fn, err);
}
static inline void seamcall_err_ret(u64 fn, u64 err,
struct tdx_module_args *args)
{
seamcall_err(fn, err, args);
pr_err("RCX 0x%016llx RDX 0x%016llx R08 0x%016llx\n",
args->rcx, args->rdx, args->r8);
pr_err("R09 0x%016llx R10 0x%016llx R11 0x%016llx\n",
args->r9, args->r10, args->r11);
}
static __always_inline int sc_retry_prerr(sc_func_t func,
sc_err_func_t err_func,
u64 fn, struct tdx_module_args *args)
{
u64 sret = sc_retry(func, fn, args);
if (sret == TDX_SUCCESS)
return 0;
if (sret == TDX_SEAMCALL_VMFAILINVALID)
return -ENODEV;
if (sret == TDX_SEAMCALL_GP)
return -EOPNOTSUPP;
if (sret == TDX_SEAMCALL_UD)
return -EACCES;
err_func(fn, sret, args);
return -EIO;
}
#define seamcall_prerr(__fn, __args) \
sc_retry_prerr(__seamcall, seamcall_err, (__fn), (__args))
#define seamcall_prerr_ret(__fn, __args) \
sc_retry_prerr(__seamcall_ret, seamcall_err_ret, (__fn), (__args))
static DEFINE_RAW_SPINLOCK(sysinit_lock);
/*
* Do the module global initialization once and return its result.
@@ -111,31 +78,34 @@ static __always_inline int sc_retry_prerr(sc_func_t func,
static int try_init_module_global(void)
{
struct tdx_module_args args = {};
static DEFINE_RAW_SPINLOCK(sysinit_lock);
static bool sysinit_done;
static int sysinit_ret;
int ret;
raw_spin_lock(&sysinit_lock);
if (sysinit_done)
/* Return the "cached" return code. */
if (tdx_module_state.sysinit_done) {
ret = tdx_module_state.sysinit_ret;
goto out;
}
/* RCX is module attributes and all bits are reserved */
args.rcx = 0;
sysinit_ret = seamcall_prerr(TDH_SYS_INIT, &args);
ret = seamcall_prerr(TDH_SYS_INIT, &args);
/*
* The first SEAMCALL also detects the TDX module, thus
* it can fail due to the TDX module is not loaded.
* Dump message to let the user know.
*/
if (sysinit_ret == -ENODEV)
if (ret == -ENODEV)
pr_err("module not loaded\n");
sysinit_done = true;
/* Save the return code for later callers. */
tdx_module_state.sysinit_done = true;
tdx_module_state.sysinit_ret = ret;
out:
raw_spin_unlock(&sysinit_lock);
return sysinit_ret;
return ret;
}
/**
@@ -143,7 +113,7 @@ out:
* (and TDX module global initialization SEAMCALL if not done) on local cpu to
* make this cpu be ready to run any other SEAMCALLs.
*/
static int tdx_cpu_enable(void)
int tdx_cpu_enable(void)
{
struct tdx_module_args args = {};
int ret;
@@ -184,6 +154,17 @@ static int tdx_online_cpu(unsigned int cpu)
return ret;
}
static void tdx_cpu_flush_cache(void)
{
lockdep_assert_preemption_disabled();
if (!this_cpu_read(cache_state_incoherent))
return;
wbinvd();
this_cpu_write(cache_state_incoherent, false);
}
static int tdx_offline_cpu(unsigned int cpu)
{
int i;
@@ -220,17 +201,34 @@ static int tdx_offline_cpu(unsigned int cpu)
return -EBUSY;
done:
/*
* Flush cache on the CPU going offline to ensure no dirty
* cachelines of TDX private memory remain. This may be
* redundant with WBINVD done elsewhere during CPU offline
* (e.g. hlt_play_dead()), but do it explicitly for safety.
*/
tdx_cpu_flush_cache();
x86_virt_put_ref(X86_FEATURE_VMX);
return 0;
}
static void tdx_shutdown_cpu(void *ign)
{
/*
* Flush cache in preparation for kexec - this is necessary to avoid
* having dirty private memory cachelines when the new kernel boots,
* but WBINVD is a relatively expensive operation and doing it during
* kexec can exacerbate races in native_stop_other_cpus(). Do it
* now, since this is a safe moment and there is going to be no more
* TDX activity on this CPU from this point on.
*/
tdx_cpu_flush_cache();
x86_virt_put_ref(X86_FEATURE_VMX);
}
static void tdx_shutdown(void *ign)
{
tdx_sys_disable();
on_each_cpu(tdx_shutdown_cpu, NULL, 1);
}
@@ -330,7 +328,7 @@ err:
return ret;
}
static __init int read_sys_metadata_field(u64 field_id, u64 *data)
static int read_sys_metadata_field(u64 field_id, u64 *data)
{
struct tdx_module_args args = {};
int ret;
@@ -1270,12 +1268,70 @@ static __init int tdx_enable(void)
register_syscore(&tdx_syscore);
tdx_module_initialized = true;
tdx_module_state.initialized = true;
pr_info("TDX-Module initialized\n");
return 0;
}
subsys_initcall(tdx_enable);
int tdx_module_shutdown(void)
{
struct tdx_sys_info_handoff handoff = {};
struct tdx_module_args args = {};
int ret;
int cpu;
ret = get_tdx_sys_info_handoff(&handoff);
/*
* Handoff information is required for proper
* shutdown. Refuse to shut down without it.
*/
if (ret)
return ret;
/*
* Use the module's handoff version as it is the highest the
* module can produce and most likely supported by newer modules.
*/
args.rcx = handoff.module_hv;
ret = seamcall_prerr(TDH_SYS_SHUTDOWN, &args);
if (ret)
return ret;
/*
* Clear global and per-CPU initialization flags so the new module
* can be fully re-initialized after a successful update.
*
* No locks needed as no concurrent accesses can occur here.
*/
memset(&tdx_module_state, 0, sizeof(tdx_module_state));
for_each_possible_cpu(cpu)
per_cpu(tdx_lp_initialized, cpu) = false;
return 0;
}
int tdx_module_run_update(void)
{
struct tdx_module_args args = {};
int ret;
ret = seamcall_prerr(TDH_SYS_UPDATE, &args);
if (ret)
return ret;
ret = get_tdx_sys_info_version(&tdx_sysinfo.version);
/*
* Only fails if there is something unexpected
* and severely wrong with the module.
*/
WARN_ON_ONCE(ret);
tdx_module_state.initialized = true;
return 0;
}
static bool is_pamt_page(unsigned long phys)
{
struct tdmr_info_list *tdmr_list = &tdx_tdmr_list;
@@ -1453,6 +1509,8 @@ static struct notifier_block tdx_memory_nb = {
static void __init check_tdx_erratum(void)
{
u64 basic_msr;
/*
* These CPUs have an erratum. A partial write from non-TD
* software (e.g. via MOVNTI variants or UC/WC mapping) to TDX
@@ -1464,6 +1522,14 @@ static void __init check_tdx_erratum(void)
case INTEL_EMERALDRAPIDS_X:
setup_force_cpu_bug(X86_BUG_TDX_PW_MCE);
}
/*
* Some TDX-capable CPUs have an erratum where the current VMCS is
* cleared after calling into P-SEAMLDR.
*/
rdmsrq(MSR_IA32_VMX_BASIC, basic_msr);
if (!(basic_msr & VMX_BASIC_NO_SEAMRET_INVD_VMCS))
setup_force_cpu_bug(X86_BUG_SEAMRET_INVD_VMCS);
}
void __init tdx_init(void)
@@ -1525,12 +1591,12 @@ void __init tdx_init(void)
const struct tdx_sys_info *tdx_get_sysinfo(void)
{
if (!tdx_module_initialized)
if (!tdx_module_state.initialized)
return NULL;
return (const struct tdx_sys_info *)&tdx_sysinfo;
}
EXPORT_SYMBOL_FOR_KVM(tdx_get_sysinfo);
EXPORT_SYMBOL_FOR_MODULES(tdx_get_sysinfo, "kvm-intel,tdx-host");
u32 tdx_get_nr_guest_keyids(void)
{
@@ -1921,21 +1987,32 @@ u64 tdh_phymem_page_wbinvd_hkid(u64 hkid, struct page *page)
}
EXPORT_SYMBOL_FOR_KVM(tdh_phymem_page_wbinvd_hkid);
#ifdef CONFIG_KEXEC_CORE
void tdx_cpu_flush_cache_for_kexec(void)
void tdx_sys_disable(void)
{
lockdep_assert_preemption_disabled();
if (!this_cpu_read(cache_state_incoherent))
return;
struct tdx_module_args args = {};
u64 ret;
/*
* Private memory cachelines need to be clean at the time of
* kexec. Write them back now, as the caller promises that
* there should be no more SEAMCALLs on this CPU.
* Don't loop forever.
*
* - TDX_INTERRUPTED_RESUMABLE guarantees forward progress between
* calls.
*
* - TDX_SYS_BUSY could be returned due to contention with other
* TDH.SYS.* SEAMCALLs, but will lock out *new* TDH.SYS.* SEAMCALLs,
* so that SYS.DISABLE can eventually make progress.
*
* This is a 'destructive' SEAMCALL, in that no other SEAMCALL can be
* run after this until a full reinitialization is done.
*/
wbinvd();
this_cpu_write(cache_state_incoherent, false);
do {
ret = seamcall(TDH_SYS_DISABLE, &args);
} while (ret == TDX_INTERRUPTED_RESUMABLE || ret == TDX_SYS_BUSY);
/*
* Print SEAMCALL failures, but not SW-defined error codes
* (SEAMCALL faulted with #GP/#UD, TDX not supported).
*/
if (ret && (ret & TDX_SW_ERROR) != TDX_SW_ERROR)
pr_err("TDH.SYS.DISABLE failed: 0x%016llx\n", ret);
}
EXPORT_SYMBOL_FOR_KVM(tdx_cpu_flush_cache_for_kexec);
#endif
+6 -3
View File
@@ -46,6 +46,9 @@
#define TDH_PHYMEM_PAGE_WBINVD 41
#define TDH_VP_WR 43
#define TDH_SYS_CONFIG 45
#define TDH_SYS_SHUTDOWN 52
#define TDH_SYS_UPDATE 53
#define TDH_SYS_DISABLE 69
/*
* SEAMCALL leaf:
@@ -84,9 +87,6 @@ struct tdmr_info {
DECLARE_FLEX_ARRAY(struct tdmr_reserved_area, reserved_areas);
} __packed __aligned(TDMR_INFO_ALIGNMENT);
/* Bit definitions of TDX_FEATURES0 metadata field */
#define TDX_FEATURES0_NO_RBP_MOD BIT(18)
/*
* Do not put any hardware-defined TDX structure representations below
* this comment!
@@ -110,4 +110,7 @@ struct tdmr_info_list {
int max_tdmrs; /* How many 'tdmr_info's are allocated */
};
int tdx_module_shutdown(void);
int tdx_module_run_update(void);
#endif
+15 -2
View File
@@ -7,7 +7,7 @@
* Include this file to other C file instead.
*/
static __init int get_tdx_sys_info_version(struct tdx_sys_info_version *sysinfo_version)
static int get_tdx_sys_info_version(struct tdx_sys_info_version *sysinfo_version)
{
int ret = 0;
u64 val;
@@ -100,13 +100,26 @@ static __init int get_tdx_sys_info_td_conf(struct tdx_sys_info_td_conf *sysinfo_
return ret;
}
static int get_tdx_sys_info_handoff(struct tdx_sys_info_handoff *sysinfo_handoff)
{
int ret;
u64 val;
ret = read_sys_metadata_field(0x8900000100000000, &val);
if (ret)
return ret;
sysinfo_handoff->module_hv = val;
return 0;
}
static __init int get_tdx_sys_info(struct tdx_sys_info *sysinfo)
{
int ret = 0;
ret = ret ?: get_tdx_sys_info_version(&sysinfo->version);
pr_info("Module version: %u.%u.%02u\n",
pr_info("Module version: " TDX_VERSION_FMT "\n",
sysinfo->version.major_version,
sysinfo->version.minor_version,
sysinfo->version.update_version);
+7
View File
@@ -181,6 +181,12 @@ static const struct apd_device_desc hip08_spi_desc = {
.setup = acpi_apd_setup,
.fixed_clk_rate = 250000000,
};
static const struct apd_device_desc leca_i2c_desc = {
.setup = acpi_apd_setup,
.fixed_clk_rate = 250000000,
};
#endif /* CONFIG_ARM64 */
#endif
@@ -251,6 +257,7 @@ static const struct acpi_device_id acpi_apd_device_ids[] = {
{ "HISI02A2", APD_ADDR(hip08_i2c_desc) },
{ "HISI02A3", APD_ADDR(hip08_lite_i2c_desc) },
{ "HISI0173", APD_ADDR(hip08_spi_desc) },
{ "LECA0003", APD_ADDR(leca_i2c_desc) },
{ "NXP0001", APD_ADDR(nxp_i2c_desc) },
#endif
{ }
+1 -1
View File
@@ -216,7 +216,7 @@ config TI_SYSC
config TS_NBUS
tristate "Technologic Systems NBUS Driver"
depends on SOC_IMX28
depends on OF_GPIO && PWM
depends on PWM
help
Driver for the Technologic Systems NBUS which is used to interface
with the peripherals in the FPGA of the TS-4600 SoM.
+3 -1
View File
@@ -1374,7 +1374,9 @@ static int __sev_snp_init_locked(int *error, unsigned int max_snp_asid)
return -EOPNOTSUPP;
}
snp_prepare();
rc = snp_prepare();
if (rc)
return rc;
/*
* Starting in SNP firmware v1.52, the SNP_INIT_EX command takes a list
+12 -5
View File
@@ -112,16 +112,20 @@ void coreboot_driver_unregister(struct coreboot_driver *driver)
}
EXPORT_SYMBOL(coreboot_driver_unregister);
static int coreboot_table_populate(struct device *dev, void *ptr)
static int coreboot_table_populate(struct device *dev, void *ptr, resource_size_t len)
{
int i, ret;
void *ptr_entry;
struct coreboot_device *device;
struct coreboot_table_entry *entry;
struct coreboot_table_header *header = ptr;
void *ptr_end;
ptr_end = ptr + len;
ptr_entry = ptr + header->header_bytes;
for (i = 0; i < header->table_entries; i++) {
if (ptr_entry + sizeof(*entry) > ptr_end)
return -EINVAL;
entry = ptr_entry;
if (entry->size < sizeof(*entry)) {
@@ -129,6 +133,9 @@ static int coreboot_table_populate(struct device *dev, void *ptr)
return -EINVAL;
}
if (ptr_entry + entry->size > ptr_end)
return -EINVAL;
device = kzalloc(sizeof(device->dev) + entry->size, GFP_KERNEL);
if (!device)
return -ENOMEM;
@@ -148,13 +155,13 @@ static int coreboot_table_populate(struct device *dev, void *ptr)
break;
}
ptr_entry += entry->size;
ret = device_register(&device->dev);
if (ret) {
dev_warn(dev, "failed to register coreboot device: %d\n", ret);
put_device(&device->dev);
return ret;
}
ptr_entry += entry->size;
}
return 0;
@@ -194,7 +201,7 @@ static int coreboot_table_probe(struct platform_device *pdev)
if (!ptr)
return -ENOMEM;
ret = coreboot_table_populate(dev, ptr);
ret = coreboot_table_populate(dev, ptr, len);
memunmap(ptr);
+25 -14
View File
@@ -102,6 +102,14 @@ config GPIO_CDEV_V1
This ABI version is deprecated.
Please use the latest ABI for new developments.
config GPIO_KUNIT
tristate "Build GPIO Kunit test cases"
depends on KUNIT
default KUNIT_ALL_TESTS
help
Say Y here to build the module containing Kunit test cases verifying
the functionality of the GPIO subsystem.
config GPIO_GENERIC
depends on HAS_IOMEM # Only for IOMEM drivers
tristate
@@ -156,6 +164,7 @@ config GPIO_74XX_MMIO
config GPIO_ALTERA
tristate "Altera GPIO"
select GPIO_GENERIC
select GPIOLIB_IRQCHIP
help
Say Y or M here to build support for the Altera PIO device.
@@ -301,7 +310,7 @@ config GPIO_EM
config GPIO_EN7523
tristate "Airoha GPIO support"
depends on ARCH_AIROHA
depends on ARCH_AIROHA || COMPILE_TEST
default ARCH_AIROHA
select GPIO_GENERIC
select GPIOLIB_IRQCHIP
@@ -698,7 +707,7 @@ config GPIO_SPACEMIT_K1
config GPIO_SPEAR_SPICS
bool "ST SPEAr13xx SPI Chip Select as GPIO support"
depends on PLAT_SPEAR
depends on PLAT_SPEAR || COMPILE_TEST
select GENERIC_IRQ_CHIP
help
Say yes here to support ST SPEAr SPI Chip Select as GPIO device.
@@ -805,8 +814,18 @@ config GPIO_VISCONTI
help
Say yes here to support GPIO on Tohisba Visconti.
config GPIO_WAVESHARE_DSI_TOUCH
tristate "Waveshare GPIO controller for DSI panels"
depends on BACKLIGHT_CLASS_DEVICE
depends on I2C
select REGMAP_I2C
help
Enable support for the GPIO and PWM controller found on Waveshare DSI
TOUCH panel kits. It provides GPIOs (used for regulator control and
resets) and backlight support.
config GPIO_WCD934X
tristate "Qualcomm Technologies Inc WCD9340/WCD9341 GPIO controller driver"
tristate "Qualcomm WCD9340/WCD9341 GPIO controller driver"
depends on MFD_WCD934X
help
This driver is to support GPIO block found on the Qualcomm Technologies
@@ -814,7 +833,7 @@ config GPIO_WCD934X
config GPIO_XGENE
bool "APM X-Gene GPIO controller support"
depends on ARM64
depends on ARM64 || COMPILE_TEST
help
This driver is to support the GPIO block within the APM X-Gene SoC
platform's generic flash controller. The GPIO pins are muxed with
@@ -858,7 +877,7 @@ config GPIO_XTENSA
config GPIO_ZEVIO
bool "LSI ZEVIO SoC memory mapped GPIOs"
depends on ARM
depends on ARM || COMPILE_TEST
help
Say yes here to support the GPIO controller in LSI ZEVIO SoCs.
@@ -1093,15 +1112,6 @@ config GPIO_SCH311X
To compile this driver as a module, choose M here: the module will
be called gpio-sch311x.
config GPIO_TS5500
tristate "TS-5500 DIO blocks and compatibles"
depends on TS5500 || COMPILE_TEST
help
This driver supports Digital I/O exposed by pin blocks found on some
Technologic Systems platforms. It includes, but is not limited to, 3
blocks of the TS-5500: DIO1, DIO2 and the LCD port, and the TS-5600
LCD port.
config GPIO_WINBOND
tristate "Winbond Super I/O GPIO support"
select ISA_BUS_API
@@ -2060,6 +2070,7 @@ config GPIO_VIRTIO
config GPIO_SIM
tristate "GPIO Simulator Module"
depends on SYSFS
select IRQ_SIM
select CONFIGFS_FS
help
+2 -1
View File
@@ -13,6 +13,7 @@ obj-$(CONFIG_GPIO_ACPI) += gpiolib-acpi.o
gpiolib-acpi-y := gpiolib-acpi-core.o gpiolib-acpi-quirks.o
obj-$(CONFIG_GPIOLIB) += gpiolib-swnode.o
obj-$(CONFIG_GPIO_SHARED) += gpiolib-shared.o
obj-$(CONFIG_GPIO_KUNIT) += gpiolib-kunit.o
# Device drivers. Generally keep list sorted alphabetically
obj-$(CONFIG_GPIO_REGMAP) += gpio-regmap.o
@@ -194,7 +195,6 @@ obj-$(CONFIG_GPIO_TPS68470) += gpio-tps68470.o
obj-$(CONFIG_GPIO_TQMX86) += gpio-tqmx86.o
obj-$(CONFIG_GPIO_TS4800) += gpio-ts4800.o
obj-$(CONFIG_GPIO_TS4900) += gpio-ts4900.o
obj-$(CONFIG_GPIO_TS5500) += gpio-ts5500.o
obj-$(CONFIG_GPIO_TWL4030) += gpio-twl4030.o
obj-$(CONFIG_GPIO_TWL6040) += gpio-twl6040.o
obj-$(CONFIG_GPIO_UNIPHIER) += gpio-uniphier.o
@@ -205,6 +205,7 @@ obj-$(CONFIG_GPIO_VIRTUSER) += gpio-virtuser.o
obj-$(CONFIG_GPIO_VIRTIO) += gpio-virtio.o
obj-$(CONFIG_GPIO_VISCONTI) += gpio-visconti.o
obj-$(CONFIG_GPIO_VX855) += gpio-vx855.o
obj-$(CONFIG_GPIO_WAVESHARE_DSI_TOUCH) += gpio-waveshare-dsi.o
obj-$(CONFIG_GPIO_WCD934X) += gpio-wcd934x.o
obj-$(CONFIG_GPIO_WHISKEY_COVE) += gpio-wcove.o
obj-$(CONFIG_GPIO_WINBOND) += gpio-winbond.o
+16 -1
View File
@@ -112,7 +112,7 @@ static int gen_74x164_probe(struct spi_device *spi)
{
struct device *dev = &spi->dev;
struct gen_74x164_chip *chip;
u32 nregs;
u32 nregs, init_state;
int ret;
/*
@@ -134,6 +134,21 @@ static int gen_74x164_probe(struct spi_device *spi)
chip->registers = nregs;
/*
* Optionally seed the chain with a board-specified pattern so the
* outputs come up in a known state on the first SPI write. The
* property follows the nxp,pcf8575 convention where bit N maps to
* GPIO line N. On this output-only device, bit=0 drives the line
* low and bit=1 drives it high. The bitmask covers up to 32 lines;
* any further outputs come up zeroed by devm_kzalloc().
*/
if (!device_property_read_u32(dev, "lines-initial-states", &init_state)) {
unsigned int i;
for (i = 0; i < min(nregs, 4U); i++)
chip->buffer[nregs - 1 - i] = (init_state >> (i * 8)) & 0xff;
}
chip->gpiod_oe = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW);
if (IS_ERR(chip->gpiod_oe))
return PTR_ERR(chip->gpiod_oe);
+2 -2
View File
@@ -501,8 +501,8 @@ static int adnp_i2c_probe(struct i2c_client *client)
}
static const struct i2c_device_id adnp_i2c_id[] = {
{ "gpio-adnp" },
{ },
{ .name = "gpio-adnp" },
{ }
};
MODULE_DEVICE_TABLE(i2c, adnp_i2c_id);
+2 -2
View File
@@ -507,8 +507,8 @@ static const struct adp5585_gpio_chip adp5589_gpio_chip_info = {
};
static const struct platform_device_id adp5585_gpio_id_table[] = {
{ "adp5585-gpio", (kernel_ulong_t)&adp5585_gpio_chip_info },
{ "adp5589-gpio", (kernel_ulong_t)&adp5589_gpio_chip_info },
{ .name = "adp5585-gpio", .driver_data = (kernel_ulong_t)&adp5585_gpio_chip_info },
{ .name = "adp5589-gpio", .driver_data = (kernel_ulong_t)&adp5589_gpio_chip_info },
{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(platform, adp5585_gpio_id_table);
+36 -88
View File
@@ -17,6 +17,7 @@
#include <linux/types.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/generic.h>
#define ALTERA_GPIO_MAX_NGPIO 32
#define ALTERA_GPIO_DATA 0x0
@@ -26,7 +27,7 @@
/**
* struct altera_gpio_chip
* @gc : GPIO chip structure.
* @chip : Generic GPIO chip structure.
* @regs : memory mapped IO address for the controller registers.
* @gpio_lock : synchronization lock so that new irq/set/get requests
* will be blocked until the current one completes.
@@ -34,7 +35,7 @@
* (rising, falling, both, high)
*/
struct altera_gpio_chip {
struct gpio_chip gc;
struct gpio_generic_chip chip;
void __iomem *regs;
raw_spinlock_t gpio_lock;
int interrupt_trigger;
@@ -106,72 +107,6 @@ static unsigned int altera_gpio_irq_startup(struct irq_data *d)
return 0;
}
static int altera_gpio_get(struct gpio_chip *gc, unsigned offset)
{
struct altera_gpio_chip *altera_gc = gpiochip_get_data(gc);
return !!(readl(altera_gc->regs + ALTERA_GPIO_DATA) & BIT(offset));
}
static int altera_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
{
struct altera_gpio_chip *altera_gc = gpiochip_get_data(gc);
unsigned long flags;
unsigned int data_reg;
raw_spin_lock_irqsave(&altera_gc->gpio_lock, flags);
data_reg = readl(altera_gc->regs + ALTERA_GPIO_DATA);
if (value)
data_reg |= BIT(offset);
else
data_reg &= ~BIT(offset);
writel(data_reg, altera_gc->regs + ALTERA_GPIO_DATA);
raw_spin_unlock_irqrestore(&altera_gc->gpio_lock, flags);
return 0;
}
static int altera_gpio_direction_input(struct gpio_chip *gc, unsigned offset)
{
struct altera_gpio_chip *altera_gc = gpiochip_get_data(gc);
unsigned long flags;
unsigned int gpio_ddr;
raw_spin_lock_irqsave(&altera_gc->gpio_lock, flags);
/* Set pin as input, assumes software controlled IP */
gpio_ddr = readl(altera_gc->regs + ALTERA_GPIO_DIR);
gpio_ddr &= ~BIT(offset);
writel(gpio_ddr, altera_gc->regs + ALTERA_GPIO_DIR);
raw_spin_unlock_irqrestore(&altera_gc->gpio_lock, flags);
return 0;
}
static int altera_gpio_direction_output(struct gpio_chip *gc,
unsigned offset, int value)
{
struct altera_gpio_chip *altera_gc = gpiochip_get_data(gc);
unsigned long flags;
unsigned int data_reg, gpio_ddr;
raw_spin_lock_irqsave(&altera_gc->gpio_lock, flags);
/* Sets the GPIO value */
data_reg = readl(altera_gc->regs + ALTERA_GPIO_DATA);
if (value)
data_reg |= BIT(offset);
else
data_reg &= ~BIT(offset);
writel(data_reg, altera_gc->regs + ALTERA_GPIO_DATA);
/* Set pin as output, assumes software controlled IP */
gpio_ddr = readl(altera_gc->regs + ALTERA_GPIO_DIR);
gpio_ddr |= BIT(offset);
writel(gpio_ddr, altera_gc->regs + ALTERA_GPIO_DIR);
raw_spin_unlock_irqrestore(&altera_gc->gpio_lock, flags);
return 0;
}
static void altera_gpio_irq_edge_handler(struct irq_desc *desc)
{
struct gpio_chip *gc = irq_desc_get_handler_data(desc);
@@ -231,9 +166,12 @@ static const struct irq_chip altera_gpio_irq_chip = {
static int altera_gpio_probe(struct platform_device *pdev)
{
struct gpio_generic_chip_config config;
struct device *dev = &pdev->dev;
int reg, ret;
struct altera_gpio_chip *altera_gc;
struct gpio_generic_chip *chip;
struct gpio_chip *gc;
struct gpio_irq_chip *girq;
int mapped_irq;
@@ -243,35 +181,45 @@ static int altera_gpio_probe(struct platform_device *pdev)
raw_spin_lock_init(&altera_gc->gpio_lock);
altera_gc->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(altera_gc->regs))
return dev_err_probe(dev, PTR_ERR(altera_gc->regs),
"failed to ioremap memory resource\n");
chip = &altera_gc->chip;
config = (struct gpio_generic_chip_config) {
.dev = dev,
.sz = 4,
.dat = altera_gc->regs + ALTERA_GPIO_DATA,
.set = altera_gc->regs + ALTERA_GPIO_DATA,
.dirout = altera_gc->regs + ALTERA_GPIO_DIR,
};
ret = gpio_generic_chip_init(chip, &config);
if (ret)
return dev_err_probe(dev, ret, "unable to init generic GPIO\n");
gc = &chip->gc;
if (device_property_read_u32(dev, "altr,ngpio", &reg))
/* By default assume maximum ngpio */
altera_gc->gc.ngpio = ALTERA_GPIO_MAX_NGPIO;
gc->ngpio = ALTERA_GPIO_MAX_NGPIO;
else
altera_gc->gc.ngpio = reg;
gc->ngpio = reg;
if (altera_gc->gc.ngpio > ALTERA_GPIO_MAX_NGPIO) {
if (gc->ngpio > ALTERA_GPIO_MAX_NGPIO) {
dev_warn(&pdev->dev,
"ngpio is greater than %d, defaulting to %d\n",
ALTERA_GPIO_MAX_NGPIO, ALTERA_GPIO_MAX_NGPIO);
altera_gc->gc.ngpio = ALTERA_GPIO_MAX_NGPIO;
gc->ngpio = ALTERA_GPIO_MAX_NGPIO;
}
altera_gc->gc.direction_input = altera_gpio_direction_input;
altera_gc->gc.direction_output = altera_gpio_direction_output;
altera_gc->gc.get = altera_gpio_get;
altera_gc->gc.set = altera_gpio_set;
altera_gc->gc.owner = THIS_MODULE;
altera_gc->gc.parent = &pdev->dev;
altera_gc->gc.base = -1;
altera_gc->gc.label = devm_kasprintf(dev, GFP_KERNEL, "%pfw", dev_fwnode(dev));
if (!altera_gc->gc.label)
gc->base = -1;
gc->label = devm_kasprintf(dev, GFP_KERNEL, "%pfw", dev_fwnode(dev));
if (!gc->label)
return -ENOMEM;
altera_gc->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(altera_gc->regs))
return dev_err_probe(dev, PTR_ERR(altera_gc->regs), "failed to ioremap memory resource\n");
mapped_irq = platform_get_irq_optional(pdev, 0);
if (mapped_irq < 0)
goto skip_irq;
@@ -283,7 +231,7 @@ static int altera_gpio_probe(struct platform_device *pdev)
}
altera_gc->interrupt_trigger = reg;
girq = &altera_gc->gc.irq;
girq = &gc->irq;
gpio_irq_chip_set_chip(girq, &altera_gpio_irq_chip);
if (altera_gc->interrupt_trigger == IRQ_TYPE_LEVEL_HIGH)
@@ -300,7 +248,7 @@ static int altera_gpio_probe(struct platform_device *pdev)
girq->parents[0] = mapped_irq;
skip_irq:
ret = devm_gpiochip_add_data(dev, &altera_gc->gc, altera_gc);
ret = devm_gpiochip_add_data(dev, gc, altera_gc);
if (ret) {
dev_err(&pdev->dev, "Failed adding memory mapped gpiochip\n");
return ret;
+2 -2
View File
@@ -59,8 +59,8 @@
* want to register another driver on the same PCI id.
*/
static const struct pci_device_id pci_tbl[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_SMBUS), 0 },
{ 0, }, /* terminate list */
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_SMBUS) },
{ }, /* terminate list */
};
MODULE_DEVICE_TABLE(pci, pci_tbl);
+2 -2
View File
@@ -263,8 +263,8 @@ static int gpo_bd72720_probe(struct platform_device *pdev)
}
static const struct platform_device_id bd72720_gpio_id[] = {
{ "bd72720-gpio" },
{ },
{ .name = "bd72720-gpio" },
{ }
};
MODULE_DEVICE_TABLE(platform, bd72720_gpio_id);
+2 -2
View File
@@ -110,8 +110,8 @@ static int bd9571mwv_gpio_probe(struct platform_device *pdev)
}
static const struct platform_device_id bd9571mwv_gpio_id_table[] = {
{ "bd9571mwv-gpio", ROHM_CHIP_TYPE_BD9571 },
{ "bd9574mwf-gpio", ROHM_CHIP_TYPE_BD9574 },
{ .name = "bd9571mwv-gpio", .driver_data = ROHM_CHIP_TYPE_BD9571 },
{ .name = "bd9574mwf-gpio", .driver_data = ROHM_CHIP_TYPE_BD9574 },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, bd9571mwv_gpio_id_table);
+2 -2
View File
@@ -196,8 +196,8 @@ static int cros_ec_gpio_probe(struct platform_device *pdev)
}
static const struct platform_device_id cros_ec_gpio_id[] = {
{ "cros-ec-gpio", 0 },
{}
{ .name = "cros-ec-gpio" },
{ }
};
MODULE_DEVICE_TABLE(platform, cros_ec_gpio_id);
+1 -1
View File
@@ -54,7 +54,7 @@ static const struct of_device_id ds4520_gpio_of_match_table[] = {
MODULE_DEVICE_TABLE(of, ds4520_gpio_of_match_table);
static const struct i2c_device_id ds4520_gpio_id_table[] = {
{ "ds4520-gpio" },
{ .name = "ds4520-gpio" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ds4520_gpio_id_table);
+1
View File
@@ -694,6 +694,7 @@ static const struct acpi_device_id dwapb_acpi_match[] = {
{"APMC0D07", GPIO_REG_OFFSET_V1},
{"APMC0D81", GPIO_REG_OFFSET_V2},
{"FUJI200A", GPIO_REG_OFFSET_V1},
{"LECA0001", GPIO_REG_OFFSET_V1},
{ }
};
MODULE_DEVICE_TABLE(acpi, dwapb_acpi_match);
+1 -2
View File
@@ -323,8 +323,7 @@ static int ep93xx_setup_irqs(struct platform_device *pdev,
}
girq->default_type = IRQ_TYPE_NONE;
/* TODO: replace with handle_bad_irq() once we are fully hierarchical */
girq->handler = handle_simple_irq;
girq->handler = handle_bad_irq;
return 0;
}
+1 -1
View File
@@ -150,7 +150,7 @@ static const __maybe_unused struct of_device_id fxl6408_dt_ids[] = {
MODULE_DEVICE_TABLE(of, fxl6408_dt_ids);
static const struct i2c_device_id fxl6408_id[] = {
{ "fxl6408" },
{ .name = "fxl6408" },
{ }
};
MODULE_DEVICE_TABLE(i2c, fxl6408_id);
+1 -1
View File
@@ -109,7 +109,7 @@ static int gw_pld_probe(struct i2c_client *client)
}
static const struct i2c_device_id gw_pld_id[] = {
{ "gw-pld", },
{ .name = "gw-pld" },
{ }
};
MODULE_DEVICE_TABLE(i2c, gw_pld_id);
+1 -6
View File
@@ -311,12 +311,7 @@ static int ixp4xx_gpio_probe(struct platform_device *pdev)
}
g->chip.gc.ngpio = 16;
g->chip.gc.label = "IXP4XX_GPIO_CHIP";
/*
* TODO: when we have migrated to device tree and all GPIOs
* are fetched using phandles, set this to -1 to get rid of
* the fixed gpiochip base.
*/
g->chip.gc.base = 0;
g->chip.gc.base = -1;
g->chip.gc.parent = &pdev->dev;
g->chip.gc.owner = THIS_MODULE;
+1 -1
View File
@@ -156,7 +156,7 @@ static int lp873x_gpio_probe(struct platform_device *pdev)
}
static const struct platform_device_id lp873x_gpio_id_table[] = {
{ "lp873x-gpio", },
{ .name = "lp873x-gpio" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, lp873x_gpio_id_table);
+1 -1
View File
@@ -171,7 +171,7 @@ static int lp87565_gpio_probe(struct platform_device *pdev)
}
static const struct platform_device_id lp87565_gpio_id_table[] = {
{ "lp87565-q1-gpio", },
{ .name = "lp87565-q1-gpio" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, lp87565_gpio_id_table);
+1 -1
View File
@@ -53,7 +53,7 @@ static void max7300_remove(struct i2c_client *client)
}
static const struct i2c_device_id max7300_id[] = {
{ "max7300" },
{ .name = "max7300" },
{ }
};
MODULE_DEVICE_TABLE(i2c, max7300_id);
+10 -10
View File
@@ -103,16 +103,16 @@ static uint64_t max732x_features[] = {
};
static const struct i2c_device_id max732x_id[] = {
{ "max7319", MAX7319 },
{ "max7320", MAX7320 },
{ "max7321", MAX7321 },
{ "max7322", MAX7322 },
{ "max7323", MAX7323 },
{ "max7324", MAX7324 },
{ "max7325", MAX7325 },
{ "max7326", MAX7326 },
{ "max7327", MAX7327 },
{ },
{ .name = "max7319", .driver_data = MAX7319 },
{ .name = "max7320", .driver_data = MAX7320 },
{ .name = "max7321", .driver_data = MAX7321 },
{ .name = "max7322", .driver_data = MAX7322 },
{ .name = "max7323", .driver_data = MAX7323 },
{ .name = "max7324", .driver_data = MAX7324 },
{ .name = "max7325", .driver_data = MAX7325 },
{ .name = "max7326", .driver_data = MAX7326 },
{ .name = "max7327", .driver_data = MAX7327 },
{ }
};
MODULE_DEVICE_TABLE(i2c, max732x_id);
+1 -1
View File
@@ -367,7 +367,7 @@ static int max77620_gpio_probe(struct platform_device *pdev)
static const struct platform_device_id max77620_gpio_devtype[] = {
{ .name = "max77620-gpio", },
{ .name = "max20024-gpio", },
{},
{ }
};
MODULE_DEVICE_TABLE(platform, max77620_gpio_devtype);
+1 -1
View File
@@ -502,7 +502,7 @@ static const struct of_device_id max77759_gpio_of_id[] = {
MODULE_DEVICE_TABLE(of, max77759_gpio_of_id);
static const struct platform_device_id max77759_gpio_platform_id[] = {
{ "max77759-gpio", },
{ .name = "max77759-gpio" },
{ }
};
MODULE_DEVICE_TABLE(platform, max77759_gpio_platform_id);
+216 -66
View File
@@ -29,8 +29,8 @@
#define GPIO_REG_EDGE 0xA0
struct mtk_gc {
struct irq_chip irq_chip;
struct gpio_generic_chip chip;
struct mtk *parent_priv;
int bank;
u32 rising;
u32 falling;
@@ -41,20 +41,32 @@ struct mtk_gc {
/**
* struct mtk - state container for
* data of the platform driver. It is 3
* separate gpio-chip each one with its
* own irq_chip.
* @dev: device instance
* separate gpio-chip having an IRQ
* linear domain shared for all of them
* @pdev: platform device instance
* @base: memory base address
* @irq_domain: IRQ linear domain shared across the three gpio chips
* @gpio_irq: irq number from the device tree
* @num_gpios: total number of gpio pins on the three gpio chips
* @gc_map: array of the gpio chips
*/
struct mtk {
struct device *dev;
struct platform_device *pdev;
void __iomem *base;
struct irq_domain *irq_domain;
int gpio_irq;
int num_gpios;
struct mtk_gc gc_map[MTK_BANK_CNT];
};
static inline struct mtk *
mt7621_gpio_gc_to_priv(struct gpio_chip *gc)
{
struct mtk_gc *bank = gpiochip_get_data(gc);
return bank->parent_priv;
}
static inline struct mtk_gc *
to_mediatek_gpio(struct gpio_chip *chip)
{
@@ -67,7 +79,7 @@ static inline void
mtk_gpio_w32(struct mtk_gc *rg, u32 offset, u32 val)
{
struct gpio_chip *gc = &rg->chip.gc;
struct mtk *mtk = gpiochip_get_data(gc);
struct mtk *mtk = mt7621_gpio_gc_to_priv(gc);
offset = (rg->bank * GPIO_BANK_STRIDE) + offset;
gpio_generic_write_reg(&rg->chip, mtk->base + offset, val);
@@ -77,41 +89,62 @@ static inline u32
mtk_gpio_r32(struct mtk_gc *rg, u32 offset)
{
struct gpio_chip *gc = &rg->chip.gc;
struct mtk *mtk = gpiochip_get_data(gc);
struct mtk *mtk = mt7621_gpio_gc_to_priv(gc);
offset = (rg->bank * GPIO_BANK_STRIDE) + offset;
return gpio_generic_read_reg(&rg->chip, mtk->base + offset);
}
static irqreturn_t
mediatek_gpio_irq_handler(int irq, void *data)
static void
mt7621_gpio_irq_bank_handler(struct mtk_gc *bank)
{
struct gpio_chip *gc = data;
struct mtk_gc *rg = to_mediatek_gpio(gc);
irqreturn_t ret = IRQ_NONE;
struct mtk *priv = bank->parent_priv;
struct irq_domain *domain = priv->irq_domain;
int hwbase = bank->chip.gc.offset;
unsigned long pending;
int bit;
unsigned int offset;
pending = mtk_gpio_r32(rg, GPIO_REG_STAT);
pending = mtk_gpio_r32(bank, GPIO_REG_STAT);
if (!pending)
return;
for_each_set_bit(bit, &pending, MTK_BANK_WIDTH) {
generic_handle_domain_irq(gc->irq.domain, bit);
mtk_gpio_w32(rg, GPIO_REG_STAT, BIT(bit));
ret |= IRQ_HANDLED;
mtk_gpio_w32(bank, GPIO_REG_STAT, pending);
for_each_set_bit(offset, &pending, MTK_BANK_WIDTH)
generic_handle_domain_irq(domain, hwbase + offset);
}
static void
mt7621_gpio_irq_handler(struct irq_desc *desc)
{
struct mtk *priv = irq_desc_get_handler_data(desc);
struct irq_chip *chip = irq_desc_get_chip(desc);
int i;
chained_irq_enter(chip, desc);
for (i = 0; i < MTK_BANK_CNT; i++) {
struct mtk_gc *bank = &priv->gc_map[i];
mt7621_gpio_irq_bank_handler(bank);
}
chained_irq_exit(chip, desc);
}
return ret;
static int
mt7621_gpio_hwirq_to_offset(irq_hw_number_t hwirq, struct mtk_gc *bank)
{
return hwirq - bank->chip.gc.offset;
}
static void
mediatek_gpio_irq_unmask(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct mtk_gc *rg = to_mediatek_gpio(gc);
int pin = d->hwirq;
struct mtk_gc *rg = gpiochip_get_data(gc);
u32 mask = mt7621_gpio_hwirq_to_offset(d->hwirq, rg);
u32 rise, fall, high, low;
gpiochip_enable_irq(gc, d->hwirq);
gpiochip_enable_irq(gc, mask);
guard(gpio_generic_lock_irqsave)(&rg->chip);
@@ -119,18 +152,18 @@ mediatek_gpio_irq_unmask(struct irq_data *d)
fall = mtk_gpio_r32(rg, GPIO_REG_FEDGE);
high = mtk_gpio_r32(rg, GPIO_REG_HLVL);
low = mtk_gpio_r32(rg, GPIO_REG_LLVL);
mtk_gpio_w32(rg, GPIO_REG_REDGE, rise | (BIT(pin) & rg->rising));
mtk_gpio_w32(rg, GPIO_REG_FEDGE, fall | (BIT(pin) & rg->falling));
mtk_gpio_w32(rg, GPIO_REG_HLVL, high | (BIT(pin) & rg->hlevel));
mtk_gpio_w32(rg, GPIO_REG_LLVL, low | (BIT(pin) & rg->llevel));
mtk_gpio_w32(rg, GPIO_REG_REDGE, rise | (BIT(mask) & rg->rising));
mtk_gpio_w32(rg, GPIO_REG_FEDGE, fall | (BIT(mask) & rg->falling));
mtk_gpio_w32(rg, GPIO_REG_HLVL, high | (BIT(mask) & rg->hlevel));
mtk_gpio_w32(rg, GPIO_REG_LLVL, low | (BIT(mask) & rg->llevel));
}
static void
mediatek_gpio_irq_mask(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct mtk_gc *rg = to_mediatek_gpio(gc);
int pin = d->hwirq;
struct mtk_gc *rg = gpiochip_get_data(gc);
u32 mask = mt7621_gpio_hwirq_to_offset(d->hwirq, rg);
u32 rise, fall, high, low;
scoped_guard(gpio_generic_lock_irqsave, &rg->chip) {
@@ -138,22 +171,21 @@ mediatek_gpio_irq_mask(struct irq_data *d)
fall = mtk_gpio_r32(rg, GPIO_REG_FEDGE);
high = mtk_gpio_r32(rg, GPIO_REG_HLVL);
low = mtk_gpio_r32(rg, GPIO_REG_LLVL);
mtk_gpio_w32(rg, GPIO_REG_FEDGE, fall & ~BIT(pin));
mtk_gpio_w32(rg, GPIO_REG_REDGE, rise & ~BIT(pin));
mtk_gpio_w32(rg, GPIO_REG_HLVL, high & ~BIT(pin));
mtk_gpio_w32(rg, GPIO_REG_LLVL, low & ~BIT(pin));
mtk_gpio_w32(rg, GPIO_REG_FEDGE, fall & ~BIT(mask));
mtk_gpio_w32(rg, GPIO_REG_REDGE, rise & ~BIT(mask));
mtk_gpio_w32(rg, GPIO_REG_HLVL, high & ~BIT(mask));
mtk_gpio_w32(rg, GPIO_REG_LLVL, low & ~BIT(mask));
}
gpiochip_disable_irq(gc, d->hwirq);
gpiochip_disable_irq(gc, mask);
}
static int
mediatek_gpio_irq_type(struct irq_data *d, unsigned int type)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct mtk_gc *rg = to_mediatek_gpio(gc);
int pin = d->hwirq;
u32 mask = BIT(pin);
struct mtk_gc *rg = gpiochip_get_data(gc);
u32 mask = BIT(mt7621_gpio_hwirq_to_offset(d->hwirq, rg));
if (type == IRQ_TYPE_PROBE) {
if ((rg->rising | rg->falling |
@@ -190,6 +222,26 @@ mediatek_gpio_irq_type(struct irq_data *d, unsigned int type)
return 0;
}
static int
mt7621_gpio_irq_reqres(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct mtk_gc *rg = gpiochip_get_data(gc);
unsigned int irq = mt7621_gpio_hwirq_to_offset(d->hwirq, rg);
return gpiochip_reqres_irq(gc, irq);
}
static void
mt7621_gpio_irq_relres(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct mtk_gc *rg = gpiochip_get_data(gc);
unsigned int irq = mt7621_gpio_hwirq_to_offset(d->hwirq, rg);
gpiochip_relres_irq(gc, irq);
}
static int
mediatek_gpio_xlate(struct gpio_chip *chip,
const struct of_phandle_args *spec, u32 *flags)
@@ -208,14 +260,123 @@ mediatek_gpio_xlate(struct gpio_chip *chip,
static const struct irq_chip mt7621_irq_chip = {
.name = "mt7621-gpio",
.irq_request_resources = mt7621_gpio_irq_reqres,
.irq_release_resources = mt7621_gpio_irq_relres,
.irq_mask_ack = mediatek_gpio_irq_mask,
.irq_mask = mediatek_gpio_irq_mask,
.irq_unmask = mediatek_gpio_irq_unmask,
.irq_set_type = mediatek_gpio_irq_type,
.flags = IRQCHIP_IMMUTABLE,
GPIOCHIP_IRQ_RESOURCE_HELPERS,
};
static void
mt7621_gpio_remove(struct platform_device *pdev)
{
struct mtk *priv = platform_get_drvdata(pdev);
int offset, virq;
if (priv->gpio_irq > 0)
irq_set_chained_handler_and_data(priv->gpio_irq, NULL, NULL);
/* Remove all IRQ mappings and delete the domain */
if (priv->irq_domain) {
for (offset = 0; offset < priv->num_gpios; offset++) {
virq = irq_find_mapping(priv->irq_domain, offset);
irq_dispose_mapping(virq);
}
irq_domain_remove(priv->irq_domain);
}
}
static struct mtk_gc *
mt7621_gpio_hwirq_to_bank(struct mtk *priv, irq_hw_number_t hwirq)
{
int i;
for (i = 0; i < MTK_BANK_CNT; i++) {
struct mtk_gc *bank = &priv->gc_map[i];
if (hwirq >= bank->chip.gc.offset &&
hwirq < (bank->chip.gc.offset + bank->chip.gc.ngpio))
return bank;
}
return NULL;
}
static int
mt7621_gpio_irq_map(struct irq_domain *d, unsigned int irq,
irq_hw_number_t hwirq)
{
struct mtk *priv = d->host_data;
struct mtk_gc *bank = mt7621_gpio_hwirq_to_bank(priv, hwirq);
struct platform_device *pdev = priv->pdev;
int ret;
if (!bank)
return -EINVAL;
dev_dbg(&pdev->dev, "Mapping irq %d for gpio line %d (bank %d)\n",
irq, (int)hwirq, bank->bank);
ret = irq_set_chip_data(irq, &bank->chip.gc);
if (ret < 0)
return ret;
irq_set_chip_and_handler(irq, &mt7621_irq_chip, handle_simple_irq);
irq_set_noprobe(irq);
return 0;
}
static void
mt7621_gpio_irq_unmap(struct irq_domain *d, unsigned int irq)
{
irq_set_chip_and_handler(irq, NULL, NULL);
irq_set_chip_data(irq, NULL);
}
static const struct irq_domain_ops mt7621_gpio_irq_domain_ops = {
.map = mt7621_gpio_irq_map,
.unmap = mt7621_gpio_irq_unmap,
.xlate = irq_domain_xlate_twocell,
};
static int
mt7621_gpio_irq_setup(struct platform_device *pdev,
struct mtk *priv)
{
struct device *dev = &pdev->dev;
priv->irq_domain = irq_domain_create_linear(dev_fwnode(dev),
priv->num_gpios,
&mt7621_gpio_irq_domain_ops,
priv);
if (!priv->irq_domain) {
dev_err(dev, "Couldn't allocate IRQ domain\n");
return -ENXIO;
}
irq_set_chained_handler_and_data(priv->gpio_irq,
mt7621_gpio_irq_handler, priv);
irq_set_status_flags(priv->gpio_irq, IRQ_DISABLE_UNLAZY);
return 0;
}
static int
mt7621_gpio_to_irq(struct gpio_chip *gc, unsigned int offset)
{
struct mtk *priv = mt7621_gpio_gc_to_priv(gc);
/* gc_offset is relative to this gpio_chip; want real offset */
int hwirq = offset + gc->offset;
if (hwirq >= priv->num_gpios)
return -ENXIO;
return irq_create_mapping(priv->irq_domain, hwirq);
}
static int
mediatek_gpio_bank_probe(struct device *dev, int bank)
{
@@ -228,6 +389,7 @@ mediatek_gpio_bank_probe(struct device *dev, int bank)
rg = &mtk->gc_map[bank];
memset(rg, 0, sizeof(*rg));
rg->parent_priv = mtk;
rg->bank = bank;
dat = mtk->base + GPIO_REG_DATA + (rg->bank * GPIO_BANK_STRIDE);
@@ -253,41 +415,17 @@ mediatek_gpio_bank_probe(struct device *dev, int bank)
rg->chip.gc.of_gpio_n_cells = 2;
rg->chip.gc.of_xlate = mediatek_gpio_xlate;
rg->chip.gc.ngpio = MTK_BANK_WIDTH;
rg->chip.gc.label = devm_kasprintf(dev, GFP_KERNEL, "%s-bank%d",
dev_name(dev), bank);
if (!rg->chip.gc.label)
return -ENOMEM;
rg->chip.gc.offset = bank * MTK_BANK_WIDTH;
if (mtk->gpio_irq > 0)
rg->chip.gc.to_irq = mt7621_gpio_to_irq;
if (mtk->gpio_irq) {
struct gpio_irq_chip *girq;
/*
* Directly request the irq here instead of passing
* a flow-handler because the irq is shared.
*/
ret = devm_request_irq(dev, mtk->gpio_irq,
mediatek_gpio_irq_handler, IRQF_SHARED,
rg->chip.gc.label, &rg->chip.gc);
if (ret) {
dev_err(dev, "Error requesting IRQ %d: %d\n",
mtk->gpio_irq, ret);
return ret;
}
girq = &rg->chip.gc.irq;
gpio_irq_chip_set_chip(girq, &mt7621_irq_chip);
/* This will let us handle the parent IRQ in the driver */
girq->parent_handler = NULL;
girq->num_parents = 0;
girq->parents = NULL;
girq->default_type = IRQ_TYPE_NONE;
girq->handler = handle_simple_irq;
}
ret = devm_gpiochip_add_data(dev, &rg->chip.gc, mtk);
ret = devm_gpiochip_add_data(dev, &rg->chip.gc, rg);
if (ret < 0) {
dev_err(dev, "Could not register gpio %d, ret=%d\n",
rg->chip.gc.ngpio, ret);
@@ -322,7 +460,8 @@ mediatek_gpio_probe(struct platform_device *pdev)
if (mtk->gpio_irq < 0)
return mtk->gpio_irq;
mtk->dev = dev;
mtk->pdev = pdev;
mtk->num_gpios = MTK_BANK_WIDTH * MTK_BANK_CNT;
platform_set_drvdata(pdev, mtk);
for (i = 0; i < MTK_BANK_CNT; i++) {
@@ -331,7 +470,17 @@ mediatek_gpio_probe(struct platform_device *pdev)
return ret;
}
if (mtk->gpio_irq > 0) {
ret = mt7621_gpio_irq_setup(pdev, mtk);
if (ret)
goto fail;
}
return 0;
fail:
mt7621_gpio_remove(pdev);
return ret;
}
static const struct of_device_id mediatek_gpio_match[] = {
@@ -342,6 +491,7 @@ MODULE_DEVICE_TABLE(of, mediatek_gpio_match);
static struct platform_driver mediatek_gpio_driver = {
.probe = mediatek_gpio_probe,
.remove = mt7621_gpio_remove,
.driver = {
.name = "mt7621_gpio",
.of_match_table = mediatek_gpio_match,
+2 -2
View File
@@ -330,13 +330,13 @@ static int gpio_set_wake_irq(struct irq_data *d, u32 enable)
ret = enable_irq_wake(port->irq_high);
else
ret = enable_irq_wake(port->irq);
port->wakeup_pads |= (1 << gpio_idx);
port->wakeup_pads |= BIT(gpio_idx);
} else {
if (port->irq_high && (gpio_idx >= 16))
ret = disable_irq_wake(port->irq_high);
else
ret = disable_irq_wake(port->irq);
port->wakeup_pads &= ~(1 << gpio_idx);
port->wakeup_pads &= ~BIT(gpio_idx);
}
return ret;
+42 -45
View File
@@ -86,49 +86,49 @@
#define PCA_CHIP_TYPE(x) ((x) & PCA_TYPE_MASK)
static const struct i2c_device_id pca953x_id[] = {
{ "pca6408", 8 | PCA953X_TYPE | PCA_INT, },
{ "pca6416", 16 | PCA953X_TYPE | PCA_INT, },
{ "pca9505", 40 | PCA953X_TYPE | PCA_INT, },
{ "pca9506", 40 | PCA953X_TYPE | PCA_INT, },
{ "pca9534", 8 | PCA953X_TYPE | PCA_INT, },
{ "pca9535", 16 | PCA953X_TYPE | PCA_INT, },
{ "pca9536", 4 | PCA953X_TYPE, },
{ "pca9537", 4 | PCA953X_TYPE | PCA_INT, },
{ "pca9538", 8 | PCA953X_TYPE | PCA_INT, },
{ "pca9539", 16 | PCA953X_TYPE | PCA_INT, },
{ "pca9554", 8 | PCA953X_TYPE | PCA_INT, },
{ "pca9555", 16 | PCA953X_TYPE | PCA_INT, },
{ "pca9556", 8 | PCA953X_TYPE, },
{ "pca9557", 8 | PCA953X_TYPE, },
{ "pca9574", 8 | PCA957X_TYPE | PCA_INT, },
{ "pca9575", 16 | PCA957X_TYPE | PCA_INT, },
{ "pca9698", 40 | PCA953X_TYPE, },
{ .name = "pca6408", .driver_data = 8 | PCA953X_TYPE | PCA_INT },
{ .name = "pca6416", .driver_data = 16 | PCA953X_TYPE | PCA_INT },
{ .name = "pca9505", .driver_data = 40 | PCA953X_TYPE | PCA_INT },
{ .name = "pca9506", .driver_data = 40 | PCA953X_TYPE | PCA_INT },
{ .name = "pca9534", .driver_data = 8 | PCA953X_TYPE | PCA_INT },
{ .name = "pca9535", .driver_data = 16 | PCA953X_TYPE | PCA_INT },
{ .name = "pca9536", .driver_data = 4 | PCA953X_TYPE },
{ .name = "pca9537", .driver_data = 4 | PCA953X_TYPE | PCA_INT },
{ .name = "pca9538", .driver_data = 8 | PCA953X_TYPE | PCA_INT },
{ .name = "pca9539", .driver_data = 16 | PCA953X_TYPE | PCA_INT },
{ .name = "pca9554", .driver_data = 8 | PCA953X_TYPE | PCA_INT },
{ .name = "pca9555", .driver_data = 16 | PCA953X_TYPE | PCA_INT },
{ .name = "pca9556", .driver_data = 8 | PCA953X_TYPE },
{ .name = "pca9557", .driver_data = 8 | PCA953X_TYPE },
{ .name = "pca9574", .driver_data = 8 | PCA957X_TYPE | PCA_INT },
{ .name = "pca9575", .driver_data = 16 | PCA957X_TYPE | PCA_INT },
{ .name = "pca9698", .driver_data = 40 | PCA953X_TYPE },
{ "pcal6408", 8 | PCA953X_TYPE | PCA_LATCH_INT, },
{ "pcal6416", 16 | PCA953X_TYPE | PCA_LATCH_INT, },
{ "pcal6524", 24 | PCA953X_TYPE | PCA_LATCH_INT, },
{ "pcal6534", 34 | PCAL653X_TYPE | PCA_LATCH_INT, },
{ "pcal9535", 16 | PCA953X_TYPE | PCA_LATCH_INT, },
{ "pcal9554b", 8 | PCA953X_TYPE | PCA_LATCH_INT, },
{ "pcal9555a", 16 | PCA953X_TYPE | PCA_LATCH_INT, },
{ .name = "pcal6408", .driver_data = 8 | PCA953X_TYPE | PCA_LATCH_INT },
{ .name = "pcal6416", .driver_data = 16 | PCA953X_TYPE | PCA_LATCH_INT },
{ .name = "pcal6524", .driver_data = 24 | PCA953X_TYPE | PCA_LATCH_INT },
{ .name = "pcal6534", .driver_data = 34 | PCAL653X_TYPE | PCA_LATCH_INT },
{ .name = "pcal9535", .driver_data = 16 | PCA953X_TYPE | PCA_LATCH_INT },
{ .name = "pcal9554b", .driver_data = 8 | PCA953X_TYPE | PCA_LATCH_INT },
{ .name = "pcal9555a", .driver_data = 16 | PCA953X_TYPE | PCA_LATCH_INT },
{ "max7310", 8 | PCA953X_TYPE, },
{ "max7312", 16 | PCA953X_TYPE | PCA_INT, },
{ "max7313", 16 | PCA953X_TYPE | PCA_INT, },
{ "max7315", 8 | PCA953X_TYPE | PCA_INT, },
{ "max7318", 16 | PCA953X_TYPE | PCA_INT, },
{ "pca6107", 8 | PCA953X_TYPE | PCA_INT, },
{ "tca6408", 8 | PCA953X_TYPE | PCA_INT, },
{ "tca6416", 16 | PCA953X_TYPE | PCA_INT, },
{ "tca6418", 18 | TCA6418_TYPE | PCA_INT, },
{ "tca6424", 24 | PCA953X_TYPE | PCA_INT, },
{ "tca9538", 8 | PCA953X_TYPE | PCA_INT, },
{ "tca9539", 16 | PCA953X_TYPE | PCA_INT, },
{ "tca9554", 8 | PCA953X_TYPE | PCA_INT, },
{ "xra1202", 8 | PCA953X_TYPE },
{ .name = "max7310", .driver_data = 8 | PCA953X_TYPE },
{ .name = "max7312", .driver_data = 16 | PCA953X_TYPE | PCA_INT },
{ .name = "max7313", .driver_data = 16 | PCA953X_TYPE | PCA_INT },
{ .name = "max7315", .driver_data = 8 | PCA953X_TYPE | PCA_INT },
{ .name = "max7318", .driver_data = 16 | PCA953X_TYPE | PCA_INT },
{ .name = "pca6107", .driver_data = 8 | PCA953X_TYPE | PCA_INT },
{ .name = "tca6408", .driver_data = 8 | PCA953X_TYPE | PCA_INT },
{ .name = "tca6416", .driver_data = 16 | PCA953X_TYPE | PCA_INT },
{ .name = "tca6418", .driver_data = 18 | TCA6418_TYPE | PCA_INT },
{ .name = "tca6424", .driver_data = 24 | PCA953X_TYPE | PCA_INT },
{ .name = "tca9538", .driver_data = 8 | PCA953X_TYPE | PCA_INT },
{ .name = "tca9539", .driver_data = 16 | PCA953X_TYPE | PCA_INT },
{ .name = "tca9554", .driver_data = 8 | PCA953X_TYPE | PCA_INT },
{ .name = "xra1202", .driver_data = 8 | PCA953X_TYPE },
{ "tcal6408", 8 | PCA953X_TYPE | PCA_LATCH_INT, },
{ "tcal6416", 16 | PCA953X_TYPE | PCA_LATCH_INT, },
{ .name = "tcal6408", .driver_data = 8 | PCA953X_TYPE | PCA_LATCH_INT },
{ .name = "tcal6416", .driver_data = 16 | PCA953X_TYPE | PCA_LATCH_INT },
{ }
};
MODULE_DEVICE_TABLE(i2c, pca953x_id);
@@ -877,11 +877,9 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d)
bitmap_or(irq_mask, chip->irq_trig_fall, chip->irq_trig_raise, gc->ngpio);
bitmap_or(irq_mask, irq_mask, chip->irq_trig_level_high, gc->ngpio);
bitmap_or(irq_mask, irq_mask, chip->irq_trig_level_low, gc->ngpio);
bitmap_complement(reg_direction, reg_direction, gc->ngpio);
bitmap_and(irq_mask, irq_mask, reg_direction, gc->ngpio);
/* Look for any newly setup interrupt */
for_each_set_bit(level, irq_mask, gc->ngpio)
for_each_andnot_bit(level, irq_mask, reg_direction, gc->ngpio)
pca953x_gpio_direction_input(&chip->gpio_chip, level);
mutex_unlock(&chip->irq_lock);
@@ -1005,8 +1003,7 @@ static bool pca953x_irq_pending(struct pca953x_chip *chip, unsigned long *pendin
bitmap_and(cur_stat, cur_stat, chip->irq_mask, gc->ngpio);
bitmap_or(pending, pending, cur_stat, gc->ngpio);
bitmap_complement(cur_stat, new_stat, gc->ngpio);
bitmap_and(cur_stat, cur_stat, reg_direction, gc->ngpio);
bitmap_andnot(cur_stat, reg_direction, new_stat, gc->ngpio);
bitmap_and(old_stat, cur_stat, chip->irq_trig_level_low, gc->ngpio);
bitmap_and(old_stat, old_stat, chip->irq_mask, gc->ngpio);
bitmap_or(pending, pending, old_stat, gc->ngpio);
+3 -3
View File
@@ -163,9 +163,9 @@ static const struct pca9570_chip_data slg7xl45106_gpio = {
};
static const struct i2c_device_id pca9570_id_table[] = {
{ "pca9570", (kernel_ulong_t)&pca9570_gpio},
{ "pca9571", (kernel_ulong_t)&pca9571_gpio },
{ "slg7xl45106", (kernel_ulong_t)&slg7xl45106_gpio },
{ .name = "pca9570", .driver_data = (kernel_ulong_t)&pca9570_gpio },
{ .name = "pca9571", .driver_data = (kernel_ulong_t)&pca9571_gpio },
{ .name = "slg7xl45106", .driver_data = (kernel_ulong_t)&slg7xl45106_gpio },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, pca9570_id_table);
+13 -13
View File
@@ -20,19 +20,19 @@
#include <linux/spinlock.h>
static const struct i2c_device_id pcf857x_id[] = {
{ "pcf8574", 8 },
{ "pcf8574a", 8 },
{ "pca8574", 8 },
{ "pca9670", 8 },
{ "pca9672", 8 },
{ "pca9674", 8 },
{ "pcf8575", 16 },
{ "pca8575", 16 },
{ "pca9671", 16 },
{ "pca9673", 16 },
{ "pca9675", 16 },
{ "max7328", 8 },
{ "max7329", 8 },
{ .name = "pcf8574", .driver_data = 8 },
{ .name = "pcf8574a", .driver_data = 8 },
{ .name = "pca8574", .driver_data = 8 },
{ .name = "pca9670", .driver_data = 8 },
{ .name = "pca9672", .driver_data = 8 },
{ .name = "pca9674", .driver_data = 8 },
{ .name = "pcf8575", .driver_data = 16 },
{ .name = "pca8575", .driver_data = 16 },
{ .name = "pca9671", .driver_data = 16 },
{ .name = "pca9673", .driver_data = 16 },
{ .name = "pca9675", .driver_data = 16 },
{ .name = "max7328", .driver_data = 8 },
{ .name = "max7329", .driver_data = 8 },
{ }
};
MODULE_DEVICE_TABLE(i2c, pcf857x_id);
+9 -9
View File
@@ -708,15 +708,15 @@ static int pxa_gpio_probe(struct platform_device *pdev)
}
static const struct platform_device_id gpio_id_table[] = {
{ "pxa25x-gpio", (unsigned long)&pxa25x_id },
{ "pxa26x-gpio", (unsigned long)&pxa26x_id },
{ "pxa27x-gpio", (unsigned long)&pxa27x_id },
{ "pxa3xx-gpio", (unsigned long)&pxa3xx_id },
{ "pxa93x-gpio", (unsigned long)&pxa93x_id },
{ "mmp-gpio", (unsigned long)&mmp_id },
{ "mmp2-gpio", (unsigned long)&mmp2_id },
{ "pxa1928-gpio", (unsigned long)&pxa1928_id },
{ },
{ .name = "pxa25x-gpio", .driver_data = (unsigned long)&pxa25x_id },
{ .name = "pxa26x-gpio", .driver_data = (unsigned long)&pxa26x_id },
{ .name = "pxa27x-gpio", .driver_data = (unsigned long)&pxa27x_id },
{ .name = "pxa3xx-gpio", .driver_data = (unsigned long)&pxa3xx_id },
{ .name = "pxa93x-gpio", .driver_data = (unsigned long)&pxa93x_id },
{ .name = "mmp-gpio", .driver_data = (unsigned long)&mmp_id },
{ .name = "mmp2-gpio", .driver_data = (unsigned long)&mmp2_id },
{ .name = "pxa1928-gpio", .driver_data = (unsigned long)&pxa1928_id },
{ }
};
static struct platform_driver pxa_gpio_driver = {
+4 -2
View File
@@ -40,16 +40,18 @@
#define REALTEK_GPIO_PORTS_PER_BANK 4
/**
* realtek_gpio_ctrl - Realtek Otto GPIO driver data
* struct realtek_gpio_ctrl - Realtek Otto GPIO driver data
*
* @chip: Associated gpio_generic_chip instance
* @base: Base address of the register block for a GPIO bank
* @cpumask_base: Base address of the per-CPU interrupt mask registers
* @cpu_irq_maskable: CPUs that can receive GPIO interrupts
* @lock: Lock for accessing the IRQ registers and values
* @intr_mask: Mask for interrupts lines
* @intr_type: Interrupt type selection
* @bank_read: Read a bank setting as a single 32-bit value
* @bank_write: Write a bank setting as a single 32-bit value
* @imr_line_pos: Bit shift of an IRQ line's IMR value.
* @line_imr_pos: Bit shift of an IRQ line's IMR value.
*
* The DIR, DATA, and ISR registers consist of four 8-bit port values, packed
* into a single 32-bit register. Use @bank_read (@bank_write) to get (assign)
+70 -4
View File
@@ -31,6 +31,7 @@ struct gpio_regmap {
unsigned int reg_clr_base;
unsigned int reg_dir_in_base;
unsigned int reg_dir_out_base;
unsigned long *fixed_direction_mask;
unsigned long *fixed_direction_output;
#ifdef CONFIG_REGMAP_IRQ
@@ -138,6 +139,20 @@ static int gpio_regmap_set_with_clear(struct gpio_chip *chip,
return regmap_write(gpio->regmap, reg, mask);
}
static bool gpio_regmap_fixed_direction(struct gpio_regmap *gpio,
unsigned int offset)
{
if (!gpio->fixed_direction_output)
return false;
/* In this case only some GPIOs are fixed as input/output */
if (gpio->fixed_direction_mask &&
!test_bit(offset, gpio->fixed_direction_mask))
return false;
return true;
}
static int gpio_regmap_get_direction(struct gpio_chip *chip,
unsigned int offset)
{
@@ -145,7 +160,7 @@ static int gpio_regmap_get_direction(struct gpio_chip *chip,
unsigned int base, val, reg, mask;
int invert, ret;
if (gpio->fixed_direction_output) {
if (gpio_regmap_fixed_direction(gpio, offset)) {
if (test_bit(offset, gpio->fixed_direction_output))
return GPIO_LINE_DIRECTION_OUT;
else
@@ -181,6 +196,22 @@ static int gpio_regmap_get_direction(struct gpio_chip *chip,
return GPIO_LINE_DIRECTION_IN;
}
static int gpio_regmap_try_direction_fixed(struct gpio_regmap *gpio,
unsigned int offset, bool output)
{
if (test_bit(offset, gpio->fixed_direction_output)) {
if (output)
return 0;
else
return -EINVAL;
} else {
if (output)
return -EINVAL;
else
return 0;
}
}
static int gpio_regmap_set_direction(struct gpio_chip *chip,
unsigned int offset, bool output)
{
@@ -188,6 +219,13 @@ static int gpio_regmap_set_direction(struct gpio_chip *chip,
unsigned int base, val, reg, mask;
int invert, ret;
/*
* If the direction is fixed, only accept the fixed
* direction in this call.
*/
if (gpio_regmap_fixed_direction(gpio, offset))
return gpio_regmap_try_direction_fixed(gpio, offset, output);
if (gpio->reg_dir_out_base) {
base = gpio_regmap_addr(gpio->reg_dir_out_base);
invert = 0;
@@ -219,6 +257,20 @@ static int gpio_regmap_direction_input(struct gpio_chip *chip,
static int gpio_regmap_direction_output(struct gpio_chip *chip,
unsigned int offset, int value)
{
struct gpio_regmap *gpio = gpiochip_get_data(chip);
int ret;
/*
* First check if this is gonna work on a fixed direction line,
* if it doesn't (i.e. this is a fixed input line), then do not
* attempt to set the output value either and just bail out.
*/
if (gpio_regmap_fixed_direction(gpio, offset)) {
ret = gpio_regmap_try_direction_fixed(gpio, offset, true);
if (ret)
return ret;
}
gpio_regmap_set(chip, offset, value);
return gpio_regmap_set_direction(chip, offset, true);
@@ -302,12 +354,23 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config
goto err_free_gpio;
}
if (config->fixed_direction_mask) {
gpio->fixed_direction_mask = bitmap_alloc(chip->ngpio,
GFP_KERNEL);
if (!gpio->fixed_direction_mask) {
ret = -ENOMEM;
goto err_free_gpio;
}
bitmap_copy(gpio->fixed_direction_mask,
config->fixed_direction_mask, chip->ngpio);
}
if (config->fixed_direction_output) {
gpio->fixed_direction_output = bitmap_alloc(chip->ngpio,
GFP_KERNEL);
if (!gpio->fixed_direction_output) {
ret = -ENOMEM;
goto err_free_gpio;
goto err_free_bitmap_dirmask;
}
bitmap_copy(gpio->fixed_direction_output,
config->fixed_direction_output, chip->ngpio);
@@ -329,7 +392,7 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config
ret = gpiochip_add_data(chip, gpio);
if (ret < 0)
goto err_free_bitmap;
goto err_free_bitmap_output;
#ifdef CONFIG_REGMAP_IRQ
if (config->regmap_irq_chip) {
@@ -355,8 +418,10 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config
err_remove_gpiochip:
gpiochip_remove(chip);
err_free_bitmap:
err_free_bitmap_output:
bitmap_free(gpio->fixed_direction_output);
err_free_bitmap_dirmask:
bitmap_free(gpio->fixed_direction_mask);
err_free_gpio:
kfree(gpio);
return ERR_PTR(ret);
@@ -376,6 +441,7 @@ void gpio_regmap_unregister(struct gpio_regmap *gpio)
gpiochip_remove(&gpio->gpio_chip);
bitmap_free(gpio->fixed_direction_output);
bitmap_free(gpio->fixed_direction_mask);
kfree(gpio);
}
EXPORT_SYMBOL_GPL(gpio_regmap_unregister);
+12 -12
View File
@@ -695,9 +695,9 @@ static ssize_t gpio_sim_device_config_dev_name_show(struct config_item *item,
pdev = dev->pdev;
if (pdev)
return sprintf(page, "%s\n", dev_name(&pdev->dev));
return sysfs_emit(page, "%s\n", dev_name(&pdev->dev));
return sprintf(page, "gpio-sim.%d\n", dev->id);
return sysfs_emit(page, "gpio-sim.%d\n", dev->id);
}
CONFIGFS_ATTR_RO(gpio_sim_device_config_, dev_name);
@@ -711,7 +711,7 @@ gpio_sim_device_config_live_show(struct config_item *item, char *page)
scoped_guard(mutex, &dev->lock)
live = gpio_sim_device_is_live(dev);
return sprintf(page, "%c\n", live ? '1' : '0');
return sysfs_emit(page, "%c\n", live ? '1' : '0');
}
static unsigned int gpio_sim_get_line_names_size(struct gpio_sim_bank *bank)
@@ -1059,7 +1059,7 @@ static int gpio_sim_emit_chip_name(struct device *dev, void *data)
return 0;
if (device_match_fwnode(dev, ctx->swnode))
return sprintf(ctx->page, "%s\n", dev_name(dev));
return sysfs_emit(ctx->page, "%s\n", dev_name(dev));
return 0;
}
@@ -1077,7 +1077,7 @@ static ssize_t gpio_sim_bank_config_chip_name_show(struct config_item *item,
return device_for_each_child(&dev->pdev->dev, &ctx,
gpio_sim_emit_chip_name);
return sprintf(page, "none\n");
return sysfs_emit(page, "none\n");
}
CONFIGFS_ATTR_RO(gpio_sim_bank_config_, chip_name);
@@ -1090,7 +1090,7 @@ gpio_sim_bank_config_label_show(struct config_item *item, char *page)
guard(mutex)(&dev->lock);
return sprintf(page, "%s\n", bank->label ?: "");
return sysfs_emit(page, "%s\n", bank->label ?: "");
}
static ssize_t gpio_sim_bank_config_label_store(struct config_item *item,
@@ -1125,7 +1125,7 @@ gpio_sim_bank_config_num_lines_show(struct config_item *item, char *page)
guard(mutex)(&dev->lock);
return sprintf(page, "%u\n", bank->num_lines);
return sysfs_emit(page, "%u\n", bank->num_lines);
}
static ssize_t
@@ -1171,7 +1171,7 @@ gpio_sim_line_config_name_show(struct config_item *item, char *page)
guard(mutex)(&dev->lock);
return sprintf(page, "%s\n", line->name ?: "");
return sysfs_emit(page, "%s\n", line->name ?: "");
}
static ssize_t gpio_sim_line_config_name_store(struct config_item *item,
@@ -1206,7 +1206,7 @@ gpio_sim_line_config_valid_show(struct config_item *item, char *page)
guard(mutex)(&dev->lock);
return sprintf(page, "%c\n", line->valid ? '1' : '0');
return sysfs_emit(page, "%c\n", line->valid ? '1' : '0');
}
static ssize_t gpio_sim_line_config_valid_store(struct config_item *item,
@@ -1244,7 +1244,7 @@ static ssize_t gpio_sim_hog_config_name_show(struct config_item *item,
guard(mutex)(&dev->lock);
return sprintf(page, "%s\n", hog->name ?: "");
return sysfs_emit(page, "%s\n", hog->name ?: "");
}
static ssize_t gpio_sim_hog_config_name_store(struct config_item *item,
@@ -1298,7 +1298,7 @@ static ssize_t gpio_sim_hog_config_direction_show(struct config_item *item,
return -EINVAL;
}
return sprintf(page, "%s\n", repr);
return sysfs_emit(page, "%s\n", repr);
}
static ssize_t
@@ -1338,7 +1338,7 @@ static ssize_t gpio_sim_hog_config_active_low_show(struct config_item *item,
guard(mutex)(&dev->lock);
return sprintf(page, "%c\n", hog->active_low ? '1' : '0');
return sysfs_emit(page, "%c\n", hog->active_low ? '1' : '0');
}
static ssize_t
+69
View File
@@ -19,6 +19,7 @@
#include <dt-bindings/gpio/tegra186-gpio.h>
#include <dt-bindings/gpio/tegra194-gpio.h>
#include <dt-bindings/gpio/tegra234-gpio.h>
#include <dt-bindings/gpio/nvidia,tegra238-gpio.h>
#include <dt-bindings/gpio/tegra241-gpio.h>
#include <dt-bindings/gpio/tegra256-gpio.h>
#include <dt-bindings/gpio/nvidia,tegra264-gpio.h>
@@ -1239,6 +1240,67 @@ static const struct tegra_gpio_soc tegra234_aon_soc = {
.has_vm_support = false,
};
#define TEGRA238_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \
TEGRA_GPIO_PORT(TEGRA238_MAIN, _name, _bank, _port, _pins)
static const struct tegra_gpio_port tegra238_main_ports[] = {
TEGRA238_MAIN_GPIO_PORT(A, 0, 0, 8),
TEGRA238_MAIN_GPIO_PORT(B, 0, 1, 5),
TEGRA238_MAIN_GPIO_PORT(C, 0, 2, 8),
TEGRA238_MAIN_GPIO_PORT(D, 0, 3, 8),
TEGRA238_MAIN_GPIO_PORT(E, 0, 4, 4),
TEGRA238_MAIN_GPIO_PORT(F, 0, 5, 8),
TEGRA238_MAIN_GPIO_PORT(G, 0, 6, 8),
TEGRA238_MAIN_GPIO_PORT(H, 0, 7, 6),
TEGRA238_MAIN_GPIO_PORT(J, 1, 0, 8),
TEGRA238_MAIN_GPIO_PORT(K, 1, 1, 4),
TEGRA238_MAIN_GPIO_PORT(L, 1, 2, 8),
TEGRA238_MAIN_GPIO_PORT(M, 1, 3, 8),
TEGRA238_MAIN_GPIO_PORT(N, 1, 4, 3),
TEGRA238_MAIN_GPIO_PORT(P, 1, 5, 8),
TEGRA238_MAIN_GPIO_PORT(Q, 1, 6, 3),
TEGRA238_MAIN_GPIO_PORT(R, 2, 0, 8),
TEGRA238_MAIN_GPIO_PORT(S, 2, 1, 8),
TEGRA238_MAIN_GPIO_PORT(T, 2, 2, 8),
TEGRA238_MAIN_GPIO_PORT(U, 2, 3, 6),
TEGRA238_MAIN_GPIO_PORT(V, 2, 4, 2),
TEGRA238_MAIN_GPIO_PORT(W, 3, 0, 8),
TEGRA238_MAIN_GPIO_PORT(X, 3, 1, 2)
};
static const struct tegra_gpio_soc tegra238_main_soc = {
.num_ports = ARRAY_SIZE(tegra238_main_ports),
.ports = tegra238_main_ports,
.name = "tegra238-gpio",
.instance = 0,
.num_irqs_per_bank = 8,
.has_vm_support = true,
};
#define TEGRA238_AON_GPIO_PORT(_name, _bank, _port, _pins) \
TEGRA_GPIO_PORT(TEGRA238_AON, _name, _bank, _port, _pins)
static const struct tegra_gpio_port tegra238_aon_ports[] = {
TEGRA238_AON_GPIO_PORT(AA, 0, 0, 8),
TEGRA238_AON_GPIO_PORT(BB, 0, 1, 1),
TEGRA238_AON_GPIO_PORT(CC, 0, 2, 8),
TEGRA238_AON_GPIO_PORT(DD, 0, 3, 8),
TEGRA238_AON_GPIO_PORT(EE, 0, 4, 6),
TEGRA238_AON_GPIO_PORT(FF, 0, 5, 8),
TEGRA238_AON_GPIO_PORT(GG, 0, 6, 8),
TEGRA238_AON_GPIO_PORT(HH, 0, 7, 4)
};
static const struct tegra_gpio_soc tegra238_aon_soc = {
.num_ports = ARRAY_SIZE(tegra238_aon_ports),
.ports = tegra238_aon_ports,
.name = "tegra238-gpio-aon",
.instance = 1,
.num_irqs_per_bank = 8,
.has_gte = true,
.has_vm_support = false,
};
#define TEGRA241_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \
TEGRA_GPIO_PORT(TEGRA241_MAIN, _name, _bank, _port, _pins)
@@ -1333,6 +1395,7 @@ static const struct tegra_gpio_soc tegra264_aon_soc = {
.name = "tegra264-gpio-aon",
.instance = 1,
.num_irqs_per_bank = 8,
.has_gte = true,
.has_vm_support = true,
};
@@ -1447,6 +1510,12 @@ static const struct of_device_id tegra186_gpio_of_match[] = {
}, {
.compatible = "nvidia,tegra234-gpio-aon",
.data = &tegra234_aon_soc
}, {
.compatible = "nvidia,tegra238-gpio",
.data = &tegra238_main_soc
}, {
.compatible = "nvidia,tegra238-gpio-aon",
.data = &tegra238_aon_soc
}, {
.compatible = "nvidia,tegra256-gpio",
.data = &tegra256_main_soc
+13 -13
View File
@@ -14,7 +14,6 @@
#include <linux/platform_device.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <linux/timb_gpio.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
@@ -225,19 +224,21 @@ static int timbgpio_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct gpio_chip *gc;
struct timbgpio *tgpio;
struct timbgpio_platform_data *pdata = dev_get_platdata(&pdev->dev);
int irq = platform_get_irq(pdev, 0);
if (!pdata || pdata->nr_pins > 32) {
dev_err(dev, "Invalid platform data\n");
return -EINVAL;
}
tgpio = devm_kzalloc(dev, sizeof(*tgpio), GFP_KERNEL);
if (!tgpio)
return -EINVAL;
tgpio->irq_base = pdata->irq_base;
gc = &tgpio->gpio;
err = device_property_read_u32(dev, "irq-base", &tgpio->irq_base);
if (err)
return err;
err = device_property_read_u32(dev, "gpio-base", &gc->base);
if (err)
return err;
spin_lock_init(&tgpio->lock);
@@ -245,8 +246,6 @@ static int timbgpio_probe(struct platform_device *pdev)
if (IS_ERR(tgpio->membase))
return PTR_ERR(tgpio->membase);
gc = &tgpio->gpio;
gc->label = dev_name(&pdev->dev);
gc->owner = THIS_MODULE;
gc->parent = &pdev->dev;
@@ -256,21 +255,22 @@ static int timbgpio_probe(struct platform_device *pdev)
gc->set = timbgpio_gpio_set;
gc->to_irq = (irq >= 0 && tgpio->irq_base > 0) ? timbgpio_to_irq : NULL;
gc->dbg_show = NULL;
gc->base = pdata->gpio_base;
gc->ngpio = pdata->nr_pins;
gc->can_sleep = false;
err = devm_gpiochip_add_data(&pdev->dev, gc, tgpio);
if (err)
return err;
if (gc->ngpio > 32)
return dev_err_probe(dev, -EINVAL, "Invalid number of pins\n");
/* make sure to disable interrupts */
iowrite32(0x0, tgpio->membase + TGPIO_IER);
if (irq < 0 || tgpio->irq_base <= 0)
return 0;
for (i = 0; i < pdata->nr_pins; i++) {
for (i = 0; i < gc->ngpio; i++) {
irq_set_chip_and_handler(tgpio->irq_base + i,
&timbgpio_irqchip, handle_simple_irq);
irq_set_chip_data(tgpio->irq_base + i, tgpio);
+1 -1
View File
@@ -112,7 +112,7 @@ static int tpic2810_probe(struct i2c_client *client)
}
static const struct i2c_device_id tpic2810_id_table[] = {
{ "tpic2810", },
{ .name = "tpic2810" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, tpic2810_id_table);
+1 -1
View File
@@ -91,7 +91,7 @@ static int tps65086_gpio_probe(struct platform_device *pdev)
}
static const struct platform_device_id tps65086_gpio_id_table[] = {
{ "tps65086-gpio", },
{ .name = "tps65086-gpio" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, tps65086_gpio_id_table);
+1 -1
View File
@@ -201,7 +201,7 @@ static const struct of_device_id tps65218_dt_match[] = {
MODULE_DEVICE_TABLE(of, tps65218_dt_match);
static const struct platform_device_id tps65218_gpio_id_table[] = {
{ "tps65218-gpio", },
{ .name = "tps65218-gpio" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, tps65218_gpio_id_table);
+2 -2
View File
@@ -249,8 +249,8 @@ static int tps65219_gpio_probe(struct platform_device *pdev)
}
static const struct platform_device_id tps6521x_gpio_id_table[] = {
{ "tps65214-gpio", TPS65214 },
{ "tps65219-gpio", TPS65219 },
{ .name = "tps65214-gpio", .driver_data = TPS65214 },
{ .name = "tps65219-gpio", .driver_data = TPS65219 },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, tps6521x_gpio_id_table);
+1 -1
View File
@@ -115,7 +115,7 @@ static int tps65912_gpio_probe(struct platform_device *pdev)
}
static const struct platform_device_id tps65912_gpio_id_table[] = {
{ "tps65912-gpio", },
{ .name = "tps65912-gpio" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, tps65912_gpio_id_table);
+1 -1
View File
@@ -175,7 +175,7 @@ static int ts4900_gpio_probe(struct i2c_client *client)
}
static const struct i2c_device_id ts4900_gpio_id_table[] = {
{ "ts4900-gpio", },
{ .name = "ts4900-gpio" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, ts4900_gpio_id_table);
-446
View File
@@ -1,446 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Digital I/O driver for Technologic Systems TS-5500
*
* Copyright (c) 2012 Savoir-faire Linux Inc.
* Vivien Didelot <vivien.didelot@savoirfairelinux.com>
*
* Technologic Systems platforms have pin blocks, exposing several Digital
* Input/Output lines (DIO). This driver aims to support single pin blocks.
* In that sense, the support is not limited to the TS-5500 blocks.
* Actually, the following platforms have DIO support:
*
* TS-5500:
* Documentation: https://docs.embeddedts.com/TS-5500
* Blocks: DIO1, DIO2 and LCD port.
*
* TS-5600:
* Documentation: https://docs.embeddedts.com/TS-5600
* Blocks: LCD port (identical to TS-5500 LCD).
*/
#include <linux/bitops.h>
#include <linux/gpio/driver.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
/* List of supported Technologic Systems platforms DIO blocks */
enum ts5500_blocks { TS5500_DIO1, TS5500_DIO2, TS5500_LCD, TS5600_LCD };
struct ts5500_priv {
const struct ts5500_dio *pinout;
struct gpio_chip gpio_chip;
spinlock_t lock;
bool strap;
u8 hwirq;
};
/*
* Hex 7D is used to control several blocks (e.g. DIO2 and LCD port).
* This flag ensures that the region has been requested by this driver.
*/
static bool hex7d_reserved;
/*
* This structure is used to describe capabilities of DIO lines,
* such as available directions and connected interrupt (if any).
*/
struct ts5500_dio {
const u8 value_addr;
const u8 value_mask;
const u8 control_addr;
const u8 control_mask;
const bool no_input;
const bool no_output;
const u8 irq;
};
#define TS5500_DIO_IN_OUT(vaddr, vbit, caddr, cbit) \
{ \
.value_addr = vaddr, \
.value_mask = BIT(vbit), \
.control_addr = caddr, \
.control_mask = BIT(cbit), \
}
#define TS5500_DIO_IN(addr, bit) \
{ \
.value_addr = addr, \
.value_mask = BIT(bit), \
.no_output = true, \
}
#define TS5500_DIO_IN_IRQ(addr, bit, _irq) \
{ \
.value_addr = addr, \
.value_mask = BIT(bit), \
.no_output = true, \
.irq = _irq, \
}
#define TS5500_DIO_OUT(addr, bit) \
{ \
.value_addr = addr, \
.value_mask = BIT(bit), \
.no_input = true, \
}
/*
* Input/Output DIO lines are programmed in groups of 4. Their values are
* available through 4 consecutive bits in a value port, whereas the direction
* of these 4 lines is driven by only 1 bit in a control port.
*/
#define TS5500_DIO_GROUP(vaddr, vbitfrom, caddr, cbit) \
TS5500_DIO_IN_OUT(vaddr, vbitfrom + 0, caddr, cbit), \
TS5500_DIO_IN_OUT(vaddr, vbitfrom + 1, caddr, cbit), \
TS5500_DIO_IN_OUT(vaddr, vbitfrom + 2, caddr, cbit), \
TS5500_DIO_IN_OUT(vaddr, vbitfrom + 3, caddr, cbit)
/*
* TS-5500 DIO1 block
*
* value control dir hw
* addr bit addr bit in out irq name pin offset
*
* 0x7b 0 0x7a 0 x x DIO1_0 1 0
* 0x7b 1 0x7a 0 x x DIO1_1 3 1
* 0x7b 2 0x7a 0 x x DIO1_2 5 2
* 0x7b 3 0x7a 0 x x DIO1_3 7 3
* 0x7b 4 0x7a 1 x x DIO1_4 9 4
* 0x7b 5 0x7a 1 x x DIO1_5 11 5
* 0x7b 6 0x7a 1 x x DIO1_6 13 6
* 0x7b 7 0x7a 1 x x DIO1_7 15 7
* 0x7c 0 0x7a 5 x x DIO1_8 4 8
* 0x7c 1 0x7a 5 x x DIO1_9 6 9
* 0x7c 2 0x7a 5 x x DIO1_10 8 10
* 0x7c 3 0x7a 5 x x DIO1_11 10 11
* 0x7c 4 x DIO1_12 12 12
* 0x7c 5 x 7 DIO1_13 14 13
*/
static const struct ts5500_dio ts5500_dio1[] = {
TS5500_DIO_GROUP(0x7b, 0, 0x7a, 0),
TS5500_DIO_GROUP(0x7b, 4, 0x7a, 1),
TS5500_DIO_GROUP(0x7c, 0, 0x7a, 5),
TS5500_DIO_IN(0x7c, 4),
TS5500_DIO_IN_IRQ(0x7c, 5, 7),
};
/*
* TS-5500 DIO2 block
*
* value control dir hw
* addr bit addr bit in out irq name pin offset
*
* 0x7e 0 0x7d 0 x x DIO2_0 1 0
* 0x7e 1 0x7d 0 x x DIO2_1 3 1
* 0x7e 2 0x7d 0 x x DIO2_2 5 2
* 0x7e 3 0x7d 0 x x DIO2_3 7 3
* 0x7e 4 0x7d 1 x x DIO2_4 9 4
* 0x7e 5 0x7d 1 x x DIO2_5 11 5
* 0x7e 6 0x7d 1 x x DIO2_6 13 6
* 0x7e 7 0x7d 1 x x DIO2_7 15 7
* 0x7f 0 0x7d 5 x x DIO2_8 4 8
* 0x7f 1 0x7d 5 x x DIO2_9 6 9
* 0x7f 2 0x7d 5 x x DIO2_10 8 10
* 0x7f 3 0x7d 5 x x DIO2_11 10 11
* 0x7f 4 x 6 DIO2_13 14 12
*/
static const struct ts5500_dio ts5500_dio2[] = {
TS5500_DIO_GROUP(0x7e, 0, 0x7d, 0),
TS5500_DIO_GROUP(0x7e, 4, 0x7d, 1),
TS5500_DIO_GROUP(0x7f, 0, 0x7d, 5),
TS5500_DIO_IN_IRQ(0x7f, 4, 6),
};
/*
* TS-5500 LCD port used as DIO block
* TS-5600 LCD port is identical
*
* value control dir hw
* addr bit addr bit in out irq name pin offset
*
* 0x72 0 0x7d 2 x x LCD_0 8 0
* 0x72 1 0x7d 2 x x LCD_1 7 1
* 0x72 2 0x7d 2 x x LCD_2 10 2
* 0x72 3 0x7d 2 x x LCD_3 9 3
* 0x72 4 0x7d 3 x x LCD_4 12 4
* 0x72 5 0x7d 3 x x LCD_5 11 5
* 0x72 6 0x7d 3 x x LCD_6 14 6
* 0x72 7 0x7d 3 x x LCD_7 13 7
* 0x73 0 x LCD_EN 5 8
* 0x73 6 x LCD_WR 6 9
* 0x73 7 x 1 LCD_RS 3 10
*/
static const struct ts5500_dio ts5500_lcd[] = {
TS5500_DIO_GROUP(0x72, 0, 0x7d, 2),
TS5500_DIO_GROUP(0x72, 4, 0x7d, 3),
TS5500_DIO_OUT(0x73, 0),
TS5500_DIO_IN(0x73, 6),
TS5500_DIO_IN_IRQ(0x73, 7, 1),
};
static inline void ts5500_set_mask(u8 mask, u8 addr)
{
u8 val = inb(addr);
val |= mask;
outb(val, addr);
}
static inline void ts5500_clear_mask(u8 mask, u8 addr)
{
u8 val = inb(addr);
val &= ~mask;
outb(val, addr);
}
static int ts5500_gpio_input(struct gpio_chip *chip, unsigned offset)
{
struct ts5500_priv *priv = gpiochip_get_data(chip);
const struct ts5500_dio line = priv->pinout[offset];
unsigned long flags;
if (line.no_input)
return -ENXIO;
if (line.no_output)
return 0;
spin_lock_irqsave(&priv->lock, flags);
ts5500_clear_mask(line.control_mask, line.control_addr);
spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
static int ts5500_gpio_get(struct gpio_chip *chip, unsigned offset)
{
struct ts5500_priv *priv = gpiochip_get_data(chip);
const struct ts5500_dio line = priv->pinout[offset];
return !!(inb(line.value_addr) & line.value_mask);
}
static int ts5500_gpio_output(struct gpio_chip *chip, unsigned offset, int val)
{
struct ts5500_priv *priv = gpiochip_get_data(chip);
const struct ts5500_dio line = priv->pinout[offset];
unsigned long flags;
if (line.no_output)
return -ENXIO;
spin_lock_irqsave(&priv->lock, flags);
if (!line.no_input)
ts5500_set_mask(line.control_mask, line.control_addr);
if (val)
ts5500_set_mask(line.value_mask, line.value_addr);
else
ts5500_clear_mask(line.value_mask, line.value_addr);
spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
static int ts5500_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
{
struct ts5500_priv *priv = gpiochip_get_data(chip);
const struct ts5500_dio line = priv->pinout[offset];
unsigned long flags;
spin_lock_irqsave(&priv->lock, flags);
if (val)
ts5500_set_mask(line.value_mask, line.value_addr);
else
ts5500_clear_mask(line.value_mask, line.value_addr);
spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
static int ts5500_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
{
struct ts5500_priv *priv = gpiochip_get_data(chip);
const struct ts5500_dio *block = priv->pinout;
const struct ts5500_dio line = block[offset];
/* Only one pin is connected to an interrupt */
if (line.irq)
return line.irq;
/* As this pin is input-only, we may strap it to another in/out pin */
if (priv->strap)
return priv->hwirq;
return -ENXIO;
}
static int ts5500_enable_irq(struct ts5500_priv *priv)
{
int ret = 0;
unsigned long flags;
spin_lock_irqsave(&priv->lock, flags);
if (priv->hwirq == 7)
ts5500_set_mask(BIT(7), 0x7a); /* DIO1_13 on IRQ7 */
else if (priv->hwirq == 6)
ts5500_set_mask(BIT(7), 0x7d); /* DIO2_13 on IRQ6 */
else if (priv->hwirq == 1)
ts5500_set_mask(BIT(6), 0x7d); /* LCD_RS on IRQ1 */
else
ret = -EINVAL;
spin_unlock_irqrestore(&priv->lock, flags);
return ret;
}
static void ts5500_disable_irq(struct ts5500_priv *priv)
{
unsigned long flags;
spin_lock_irqsave(&priv->lock, flags);
if (priv->hwirq == 7)
ts5500_clear_mask(BIT(7), 0x7a); /* DIO1_13 on IRQ7 */
else if (priv->hwirq == 6)
ts5500_clear_mask(BIT(7), 0x7d); /* DIO2_13 on IRQ6 */
else if (priv->hwirq == 1)
ts5500_clear_mask(BIT(6), 0x7d); /* LCD_RS on IRQ1 */
else
dev_err(priv->gpio_chip.parent, "invalid hwirq %d\n",
priv->hwirq);
spin_unlock_irqrestore(&priv->lock, flags);
}
static int ts5500_dio_probe(struct platform_device *pdev)
{
enum ts5500_blocks block = platform_get_device_id(pdev)->driver_data;
struct device *dev = &pdev->dev;
const char *name = dev_name(dev);
struct ts5500_priv *priv;
unsigned long flags;
int ret;
ret = platform_get_irq(pdev, 0);
if (ret < 0)
return ret;
priv = devm_kzalloc(dev, sizeof(struct ts5500_priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
platform_set_drvdata(pdev, priv);
priv->hwirq = ret;
spin_lock_init(&priv->lock);
priv->gpio_chip.owner = THIS_MODULE;
priv->gpio_chip.label = name;
priv->gpio_chip.parent = dev;
priv->gpio_chip.direction_input = ts5500_gpio_input;
priv->gpio_chip.direction_output = ts5500_gpio_output;
priv->gpio_chip.get = ts5500_gpio_get;
priv->gpio_chip.set = ts5500_gpio_set;
priv->gpio_chip.to_irq = ts5500_gpio_to_irq;
priv->gpio_chip.base = -1;
switch (block) {
case TS5500_DIO1:
priv->pinout = ts5500_dio1;
priv->gpio_chip.ngpio = ARRAY_SIZE(ts5500_dio1);
if (!devm_request_region(dev, 0x7a, 3, name)) {
dev_err(dev, "failed to request %s ports\n", name);
return -EBUSY;
}
break;
case TS5500_DIO2:
priv->pinout = ts5500_dio2;
priv->gpio_chip.ngpio = ARRAY_SIZE(ts5500_dio2);
if (!devm_request_region(dev, 0x7e, 2, name)) {
dev_err(dev, "failed to request %s ports\n", name);
return -EBUSY;
}
if (hex7d_reserved)
break;
if (!devm_request_region(dev, 0x7d, 1, name)) {
dev_err(dev, "failed to request %s 7D\n", name);
return -EBUSY;
}
hex7d_reserved = true;
break;
case TS5500_LCD:
case TS5600_LCD:
priv->pinout = ts5500_lcd;
priv->gpio_chip.ngpio = ARRAY_SIZE(ts5500_lcd);
if (!devm_request_region(dev, 0x72, 2, name)) {
dev_err(dev, "failed to request %s ports\n", name);
return -EBUSY;
}
if (!hex7d_reserved) {
if (!devm_request_region(dev, 0x7d, 1, name)) {
dev_err(dev, "failed to request %s 7D\n", name);
return -EBUSY;
}
hex7d_reserved = true;
}
/* Ensure usage of LCD port as DIO */
spin_lock_irqsave(&priv->lock, flags);
ts5500_clear_mask(BIT(4), 0x7d);
spin_unlock_irqrestore(&priv->lock, flags);
break;
}
ret = devm_gpiochip_add_data(dev, &priv->gpio_chip, priv);
if (ret) {
dev_err(dev, "failed to register the gpio chip\n");
return ret;
}
ret = ts5500_enable_irq(priv);
if (ret) {
dev_err(dev, "invalid interrupt %d\n", priv->hwirq);
return ret;
}
return 0;
}
static void ts5500_dio_remove(struct platform_device *pdev)
{
struct ts5500_priv *priv = platform_get_drvdata(pdev);
ts5500_disable_irq(priv);
}
static const struct platform_device_id ts5500_dio_ids[] = {
{ "ts5500-dio1", TS5500_DIO1 },
{ "ts5500-dio2", TS5500_DIO2 },
{ "ts5500-dio-lcd", TS5500_LCD },
{ "ts5600-dio-lcd", TS5600_LCD },
{ }
};
MODULE_DEVICE_TABLE(platform, ts5500_dio_ids);
static struct platform_driver ts5500_dio_driver = {
.driver = {
.name = "ts5500-dio",
},
.probe = ts5500_dio_probe,
.remove = ts5500_dio_remove,
.id_table = ts5500_dio_ids,
};
module_platform_driver(ts5500_dio_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Savoir-faire Linux Inc. <kernel@savoirfairelinux.com>");
MODULE_DESCRIPTION("Technologic Systems TS-5500 Digital I/O driver");
+1
View File
@@ -31,6 +31,7 @@ static const struct acpi_device_id usbio_gpio_acpi_hids[] = {
{ "INTC10B5" }, /* LNL */
{ "INTC10D1" }, /* MTL-CVF */
{ "INTC10E2" }, /* PTL */
{ "INTC1116" }, /* NVL */
{ }
};
+208
View File
@@ -0,0 +1,208 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2024 Waveshare International Limited
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include <linux/backlight.h>
#include <linux/err.h>
#include <linux/fb.h>
#include <linux/gpio/driver.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/regmap.h>
/* I2C registers of the microcontroller. */
#define REG_TP 0x94
#define REG_LCD 0x95
#define REG_PWM 0x96
#define REG_SIZE 0x97
#define REG_ID 0x98
#define REG_VERSION 0x99
enum {
GPIO_AVDD = 0,
GPIO_PANEL_RESET = 1,
GPIO_BL_ENABLE = 2,
GPIO_IOVCC = 4,
GPIO_VCC = 8,
GPIO_TS_RESET = 9,
};
#define NUM_GPIO 16
struct waveshare_gpio {
struct mutex dir_lock;
struct mutex pwr_lock;
struct regmap *regmap;
u16 poweron_state;
struct gpio_chip gc;
};
static const struct regmap_config waveshare_gpio_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = REG_VERSION,
};
static int waveshare_gpio_get(struct waveshare_gpio *state, unsigned int offset)
{
u16 pwr_state;
guard(mutex)(&state->pwr_lock);
pwr_state = state->poweron_state & BIT(offset);
return !!pwr_state;
}
static int waveshare_gpio_set(struct waveshare_gpio *state, unsigned int offset, int value)
{
u16 last_val;
int err;
guard(mutex)(&state->pwr_lock);
last_val = state->poweron_state;
if (value)
last_val |= BIT(offset);
else
last_val &= ~BIT(offset);
state->poweron_state = last_val;
err = regmap_write(state->regmap, REG_TP, last_val >> 8);
if (!err)
err = regmap_write(state->regmap, REG_LCD, last_val & 0xff);
return err;
}
static int waveshare_gpio_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
{
return GPIO_LINE_DIRECTION_OUT;
}
static int waveshare_gpio_gpio_get(struct gpio_chip *gc, unsigned int offset)
{
struct waveshare_gpio *state = gpiochip_get_data(gc);
return waveshare_gpio_get(state, offset);
}
static int waveshare_gpio_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
{
struct waveshare_gpio *state = gpiochip_get_data(gc);
return waveshare_gpio_set(state, offset, value);
}
static int waveshare_gpio_update_status(struct backlight_device *bl)
{
struct waveshare_gpio *state = bl_get_data(bl);
int brightness = backlight_get_brightness(bl);
waveshare_gpio_set(state, GPIO_BL_ENABLE, brightness);
return regmap_write(state->regmap, REG_PWM, brightness);
}
static const struct backlight_ops waveshare_gpio_bl = {
.update_status = waveshare_gpio_update_status,
};
static int waveshare_gpio_probe(struct i2c_client *i2c)
{
struct backlight_properties props = {};
struct waveshare_gpio *state;
struct device *dev = &i2c->dev;
struct backlight_device *bl;
struct regmap *regmap;
unsigned int data;
int ret;
state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
if (!state)
return -ENOMEM;
ret = devm_mutex_init(dev, &state->dir_lock);
if (ret)
return ret;
ret = devm_mutex_init(dev, &state->pwr_lock);
if (ret)
return ret;
regmap = devm_regmap_init_i2c(i2c, &waveshare_gpio_regmap_config);
if (IS_ERR(regmap))
return dev_err_probe(dev, PTR_ERR(regmap), "Failed to allocate register map\n");
state->regmap = regmap;
i2c_set_clientdata(i2c, state);
ret = regmap_read(regmap, REG_ID, &data);
if (ret < 0)
return dev_err_probe(dev, ret, "Failed to read register\n");
dev_dbg(dev, "waveshare panel hw id = 0x%x\n", data);
ret = regmap_read(regmap, REG_SIZE, &data);
if (ret < 0)
return dev_err_probe(dev, ret, "Failed to read register\n");
dev_dbg(dev, "waveshare panel size = %d\n", data);
ret = regmap_read(regmap, REG_VERSION, &data);
if (ret < 0)
return dev_err_probe(dev, ret, "Failed to read register\n");
dev_dbg(dev, "waveshare panel mcu version = 0x%x\n", data);
ret = waveshare_gpio_set(state, GPIO_TS_RESET, 1);
if (ret)
return dev_err_probe(dev, ret, "Failed to program GPIOs\n");
msleep(20);
state->gc.parent = dev;
state->gc.label = i2c->name;
state->gc.owner = THIS_MODULE;
state->gc.base = -1;
state->gc.ngpio = NUM_GPIO;
/* it is output only */
state->gc.get = waveshare_gpio_gpio_get;
state->gc.set = waveshare_gpio_gpio_set;
state->gc.get_direction = waveshare_gpio_gpio_get_direction;
state->gc.can_sleep = true;
ret = devm_gpiochip_add_data(dev, &state->gc, state);
if (ret)
return dev_err_probe(dev, ret, "Failed to create gpiochip\n");
props.type = BACKLIGHT_RAW;
props.max_brightness = 255;
props.brightness = 255;
bl = devm_backlight_device_register(dev, dev_name(dev), dev, state,
&waveshare_gpio_bl, &props);
return PTR_ERR_OR_ZERO(bl);
}
static const struct of_device_id waveshare_gpio_dt_ids[] = {
{ .compatible = "waveshare,dsi-touch-gpio" },
{},
};
MODULE_DEVICE_TABLE(of, waveshare_gpio_dt_ids);
static struct i2c_driver waveshare_gpio_regulator_driver = {
.driver = {
.name = "waveshare-regulator",
.of_match_table = of_match_ptr(waveshare_gpio_dt_ids),
},
.probe = waveshare_gpio_probe,
};
module_i2c_driver(waveshare_gpio_regulator_driver);
MODULE_DESCRIPTION("GPIO controller driver for Waveshare DSI touch panels");
MODULE_LICENSE("GPL");
+2 -4
View File
@@ -495,13 +495,11 @@ static void xgpio_irqhandler(struct irq_desc *desc)
xgpio_read_ch_all(chip, XGPIO_DATA_OFFSET, hw);
bitmap_complement(rising, chip->last_irq_read, 64);
bitmap_and(rising, rising, hw, 64);
bitmap_andnot(rising, hw, chip->last_irq_read, 64);
bitmap_and(rising, rising, chip->enable, 64);
bitmap_and(rising, rising, chip->rising_edge, 64);
bitmap_complement(falling, hw, 64);
bitmap_and(falling, falling, chip->last_irq_read, 64);
bitmap_andnot(falling, chip->last_irq_read, hw, 64);
bitmap_and(falling, falling, chip->enable, 64);
bitmap_and(falling, falling, chip->falling_edge, 64);
+2 -2
View File
@@ -64,14 +64,14 @@ static inline u32 zevio_gpio_port_get(struct zevio_gpio *c, unsigned pin,
unsigned port_offset)
{
unsigned section_offset = ((pin >> 3) & 3)*ZEVIO_GPIO_SECTION_SIZE;
return readl(IOMEM(c->regs + section_offset + port_offset));
return readl(c->regs + section_offset + port_offset);
}
static inline void zevio_gpio_port_set(struct zevio_gpio *c, unsigned pin,
unsigned port_offset, u32 val)
{
unsigned section_offset = ((pin >> 3) & 3)*ZEVIO_GPIO_SECTION_SIZE;
writel(val, IOMEM(c->regs + section_offset + port_offset));
writel(val, c->regs + section_offset + port_offset);
}
/* Functions for struct gpio_chip */
+12
View File
@@ -25,6 +25,7 @@
#define VERSAL_GPIO_MAX_BANK 4
#define PMC_GPIO_MAX_BANK 5
#define VERSAL_UNUSED_BANKS 2
#define EIO_GPIO_MAX_BANK 2
#define ZYNQ_GPIO_BANK0_NGPIO 32
#define ZYNQ_GPIO_BANK1_NGPIO 22
@@ -818,6 +819,16 @@ static const struct dev_pm_ops zynq_gpio_dev_pm_ops = {
RUNTIME_PM_OPS(zynq_gpio_runtime_suspend, zynq_gpio_runtime_resume, NULL)
};
static const struct zynq_platform_data eio_gpio_def = {
.label = "eio_gpio",
.ngpio = 52,
.max_bank = EIO_GPIO_MAX_BANK,
.bank_min[0] = 0,
.bank_max[0] = 25, /* 0 to 25 are connected to MIOs (26 pins) */
.bank_min[1] = 26,
.bank_max[1] = 51, /* Bank 1 are connected to MIOs (26 pins) */
};
static const struct zynq_platform_data versal_gpio_def = {
.label = "versal_gpio",
.quirks = GPIO_QUIRK_VERSAL,
@@ -882,6 +893,7 @@ static const struct of_device_id zynq_gpio_of_match[] = {
{ .compatible = "xlnx,zynqmp-gpio-1.0", .data = &zynqmp_gpio_def },
{ .compatible = "xlnx,versal-gpio-1.0", .data = &versal_gpio_def },
{ .compatible = "xlnx,pmc-gpio-1.0", .data = &pmc_gpio_def },
{ .compatible = "xlnx,eio-gpio-1.0", .data = &eio_gpio_def },
{ /* end of table */ }
};
MODULE_DEVICE_TABLE(of, zynq_gpio_of_match);
-4
View File
@@ -142,10 +142,6 @@ static struct gpio_desc *acpi_get_gpiod(char *path, unsigned int pin)
if (!gdev)
return ERR_PTR(-EPROBE_DEFER);
/*
* FIXME: keep track of the reference to the GPIO device somehow
* instead of putting it here.
*/
return gpio_device_get_desc(gdev, pin);
}
+7
View File
@@ -353,6 +353,13 @@ int devm_gpiochip_add_data_with_key(struct device *dev, struct gpio_chip *gc, vo
{
int ret;
/*
* We are passing the devres device here so if the user did not pass
* another parent, it's this one.
*/
if (!gc->parent)
gc->parent = dev;
ret = gpiochip_add_data_with_key(gc, data, lock_key, request_key);
if (ret < 0)
return ret;
Loaded 100 of 184 files, more files were not shown because too many files have changed in this diff. Show more