Merge c399c85d60 ("Merge tag 'pci-v5.19-fixes-1' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci") into android-mainline
Steps on the way to 5.19-rc1 Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> Change-Id: I607f68c16c324952bf04ead11244d03b7502f25a
This commit is contained in:
commit
2f60073848
243 changed files with 7855 additions and 4489 deletions
1
.mailmap
1
.mailmap
|
|
@ -236,6 +236,7 @@ Linus Lüssing <linus.luessing@c0d3.blue> <linus.luessing@web.de>
|
|||
<linux-hardening@vger.kernel.org> <kernel-hardening@lists.openwall.com>
|
||||
Li Yang <leoyang.li@nxp.com> <leoli@freescale.com>
|
||||
Li Yang <leoyang.li@nxp.com> <leo@zh-kernel.org>
|
||||
Lorenzo Pieralisi <lpieralisi@kernel.org> <lorenzo.pieralisi@arm.com>
|
||||
Lukasz Luba <lukasz.luba@arm.com> <l.luba@partner.samsung.com>
|
||||
Maciej W. Rozycki <macro@mips.com> <macro@imgtec.com>
|
||||
Maciej W. Rozycki <macro@orcam.me.uk> <macro@linux-mips.org>
|
||||
|
|
|
|||
|
|
@ -36,10 +36,9 @@ administrative requirements that require particular behavior that does not
|
|||
work well as part of an nfs_client_id4 string.
|
||||
|
||||
The nfs.nfs4_unique_id boot parameter specifies a unique string that can be
|
||||
used instead of a system's node name when an NFS client identifies itself to
|
||||
a server. Thus, if the system's node name is not unique, or it changes, its
|
||||
nfs.nfs4_unique_id stays the same, preventing collision with other clients
|
||||
or loss of state during NFS reboot recovery or transparent state migration.
|
||||
used together with a system's node name when an NFS client identifies itself to
|
||||
a server. Thus, if the system's node name is not unique, its
|
||||
nfs.nfs4_unique_id can help prevent collisions with other clients.
|
||||
|
||||
The nfs.nfs4_unique_id string is typically a UUID, though it can contain
|
||||
anything that is believed to be unique across all NFS clients. An
|
||||
|
|
@ -53,8 +52,12 @@ outstanding NFSv4 state has expired, to prevent loss of NFSv4 state.
|
|||
|
||||
This string can be stored in an NFS client's grub.conf, or it can be provided
|
||||
via a net boot facility such as PXE. It may also be specified as an nfs.ko
|
||||
module parameter. Specifying a uniquifier string is not support for NFS
|
||||
clients running in containers.
|
||||
module parameter.
|
||||
|
||||
This uniquifier string will be the same for all NFS clients running in
|
||||
containers unless it is overridden by a value written to
|
||||
/sys/fs/nfs/net/nfs_client/identifier which will be local to the network
|
||||
namespace of the process which writes.
|
||||
|
||||
|
||||
The DNS resolver
|
||||
|
|
|
|||
|
|
@ -1,43 +0,0 @@
|
|||
Bindings for cadence I3C master block
|
||||
=====================================
|
||||
|
||||
Required properties:
|
||||
--------------------
|
||||
- compatible: shall be "cdns,i3c-master"
|
||||
- clocks: shall reference the pclk and sysclk
|
||||
- clock-names: shall contain "pclk" and "sysclk"
|
||||
- interrupts: the interrupt line connected to this I3C master
|
||||
- reg: I3C master registers
|
||||
|
||||
Mandatory properties defined by the generic binding (see
|
||||
Documentation/devicetree/bindings/i3c/i3c.yaml for more details):
|
||||
|
||||
- #address-cells: shall be set to 1
|
||||
- #size-cells: shall be set to 0
|
||||
|
||||
Optional properties defined by the generic binding (see
|
||||
Documentation/devicetree/bindings/i3c/i3c.yaml for more details):
|
||||
|
||||
- i2c-scl-hz
|
||||
- i3c-scl-hz
|
||||
|
||||
I3C device connected on the bus follow the generic description (see
|
||||
Documentation/devicetree/bindings/i3c/i3c.yaml for more details).
|
||||
|
||||
Example:
|
||||
|
||||
i3c-master@0d040000 {
|
||||
compatible = "cdns,i3c-master";
|
||||
clocks = <&coreclock>, <&i3csysclock>;
|
||||
clock-names = "pclk", "sysclk";
|
||||
interrupts = <3 0>;
|
||||
reg = <0x0d040000 0x1000>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
i2c-scl-hz = <100000>;
|
||||
|
||||
nunchuk: nunchuk@52 {
|
||||
compatible = "nintendo,nunchuk";
|
||||
reg = <0x52 0x0 0x10>;
|
||||
};
|
||||
};
|
||||
60
Documentation/devicetree/bindings/i3c/cdns,i3c-master.yaml
Normal file
60
Documentation/devicetree/bindings/i3c/cdns,i3c-master.yaml
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/i3c/cdns,i3c-master.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Cadence I3C master block
|
||||
|
||||
maintainers:
|
||||
- Boris Brezillon <bbrezillon@kernel.org>
|
||||
|
||||
allOf:
|
||||
- $ref: i3c.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: cdns,i3c-master
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 2
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: pclk
|
||||
- const: sysclk
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- clock-names
|
||||
- interrupts
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i3c-master@d040000 {
|
||||
compatible = "cdns,i3c-master";
|
||||
clocks = <&coreclock>, <&i3csysclock>;
|
||||
clock-names = "pclk", "sysclk";
|
||||
interrupts = <3 0>;
|
||||
reg = <0x0d040000 0x1000>;
|
||||
#address-cells = <3>;
|
||||
#size-cells = <0>;
|
||||
i2c-scl-hz = <100000>;
|
||||
|
||||
eeprom@57{
|
||||
compatible = "atmel,24c01";
|
||||
reg = <0x57 0x0 0x10>;
|
||||
pagesize = <0x8>;
|
||||
};
|
||||
};
|
||||
...
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
Bindings for Synopsys DesignWare I3C master block
|
||||
=================================================
|
||||
|
||||
Required properties:
|
||||
--------------------
|
||||
- compatible: shall be "snps,dw-i3c-master-1.00a"
|
||||
- clocks: shall reference the core_clk
|
||||
- interrupts: the interrupt line connected to this I3C master
|
||||
- reg: Offset and length of I3C master registers
|
||||
|
||||
Mandatory properties defined by the generic binding (see
|
||||
Documentation/devicetree/bindings/i3c/i3c.yaml for more details):
|
||||
|
||||
- #address-cells: shall be set to 3
|
||||
- #size-cells: shall be set to 0
|
||||
|
||||
Optional properties defined by the generic binding (see
|
||||
Documentation/devicetree/bindings/i3c/i3c.yaml for more details):
|
||||
|
||||
- i2c-scl-hz
|
||||
- i3c-scl-hz
|
||||
|
||||
I3C device connected on the bus follow the generic description (see
|
||||
Documentation/devicetree/bindings/i3c/i3c.yaml for more details).
|
||||
|
||||
Example:
|
||||
|
||||
i3c-master@2000 {
|
||||
compatible = "snps,dw-i3c-master-1.00a";
|
||||
#address-cells = <3>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x02000 0x1000>;
|
||||
interrupts = <0>;
|
||||
clocks = <&i3cclk>;
|
||||
|
||||
eeprom@57{
|
||||
compatible = "atmel,24c01";
|
||||
reg = <0x57 0x0 0x10>;
|
||||
pagesize = <0x8>;
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/i3c/snps,dw-i3c-master.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Synopsys DesignWare I3C master block
|
||||
|
||||
maintainers:
|
||||
- Alexandre Belloni <alexandre.belloni@bootlin.com>
|
||||
|
||||
allOf:
|
||||
- $ref: i3c.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: snps,dw-i3c-master-1.00a
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- interrupts
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i3c-master@2000 {
|
||||
compatible = "snps,dw-i3c-master-1.00a";
|
||||
#address-cells = <3>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x02000 0x1000>;
|
||||
interrupts = <0>;
|
||||
clocks = <&i3cclk>;
|
||||
|
||||
eeprom@57{
|
||||
compatible = "atmel,24c01";
|
||||
reg = <0x57 0x0 0x10>;
|
||||
pagesize = <0x8>;
|
||||
};
|
||||
};
|
||||
...
|
||||
|
|
@ -64,10 +64,13 @@ Sub-nodes:
|
|||
and KEY_SLEEP.
|
||||
|
||||
- watchdog : This node defines settings for the Watchdog timer associated
|
||||
with the DA9063 and DA9063L. There are currently no entries in this
|
||||
binding, however compatible = "dlg,da9063-watchdog" should be added
|
||||
if a node is created.
|
||||
with the DA9063 and DA9063L. The node should contain the compatible property
|
||||
with the value "dlg,da9063-watchdog".
|
||||
|
||||
Optional watchdog properties:
|
||||
- dlg,use-sw-pm: Add this property to disable the watchdog during suspend.
|
||||
Only use this option if you can't use the watchdog automatic suspend
|
||||
function during a suspend (see register CONTROL_B).
|
||||
|
||||
Example:
|
||||
|
||||
|
|
|
|||
47
Documentation/devicetree/bindings/pwm/atmel,at91sam-pwm.yaml
Normal file
47
Documentation/devicetree/bindings/pwm/atmel,at91sam-pwm.yaml
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
# Copyright (C) 2022 Microchip Technology, Inc. and its subsidiaries
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/pwm/atmel,at91sam-pwm.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Atmel/Microchip PWM controller
|
||||
|
||||
maintainers:
|
||||
- Claudiu Beznea <claudiu.beznea@microchip.com>
|
||||
|
||||
allOf:
|
||||
- $ref: "pwm.yaml#"
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- items:
|
||||
- enum:
|
||||
- atmel,at91sam9rl-pwm
|
||||
- atmel,sama5d3-pwm
|
||||
- atmel,sama5d2-pwm
|
||||
- microchip,sam9x60-pwm
|
||||
- items:
|
||||
- const: microchip,sama7g5-pwm
|
||||
- const: atmel,sama5d2-pwm
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
"#pwm-cells":
|
||||
const: 3
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
pwm0: pwm@f8034000 {
|
||||
compatible = "atmel,at91sam9rl-pwm";
|
||||
reg = <0xf8034000 0x400>;
|
||||
#pwm-cells = <3>;
|
||||
};
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
Atmel PWM controller
|
||||
|
||||
Required properties:
|
||||
- compatible: should be one of:
|
||||
- "atmel,at91sam9rl-pwm"
|
||||
- "atmel,sama5d3-pwm"
|
||||
- "atmel,sama5d2-pwm"
|
||||
- "microchip,sam9x60-pwm"
|
||||
- reg: physical base address and length of the controller's registers
|
||||
- #pwm-cells: Should be 3. See pwm.yaml in this directory for a
|
||||
description of the cells format.
|
||||
|
||||
Example:
|
||||
|
||||
pwm0: pwm@f8034000 {
|
||||
compatible = "atmel,at91sam9rl-pwm";
|
||||
reg = <0xf8034000 0x400>;
|
||||
#pwm-cells = <3>;
|
||||
};
|
||||
|
||||
pwmleds {
|
||||
compatible = "pwm-leds";
|
||||
|
||||
d1 {
|
||||
label = "d1";
|
||||
pwms = <&pwm0 3 5000 0>
|
||||
max-brightness = <255>;
|
||||
};
|
||||
|
||||
d2 {
|
||||
label = "d2";
|
||||
pwms = <&pwm0 1 5000 1>
|
||||
max-brightness = <255>;
|
||||
};
|
||||
};
|
||||
|
|
@ -21,7 +21,14 @@ allOf:
|
|||
|
||||
properties:
|
||||
compatible:
|
||||
const: google,cros-ec-pwm
|
||||
oneOf:
|
||||
- description: PWM controlled using EC_PWM_TYPE_GENERIC channels.
|
||||
items:
|
||||
- const: google,cros-ec-pwm
|
||||
- description: PWM controlled using CROS_EC_PWM_DT_<...> types.
|
||||
items:
|
||||
- const: google,cros-ec-pwm-type
|
||||
|
||||
"#pwm-cells":
|
||||
description: The cell specifies the PWM index.
|
||||
const: 1
|
||||
|
|
|
|||
75
Documentation/devicetree/bindings/pwm/mediatek,pwm-disp.yaml
Normal file
75
Documentation/devicetree/bindings/pwm/mediatek,pwm-disp.yaml
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/pwm/mediatek,pwm-disp.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: MediaTek DISP_PWM Controller Device Tree Bindings
|
||||
|
||||
maintainers:
|
||||
- Jitao Shi <jitao.shi@mediatek.com>
|
||||
- Xinlei Lee <xinlei.lee@mediatek.com>
|
||||
|
||||
allOf:
|
||||
- $ref: pwm.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- enum:
|
||||
- mediatek,mt2701-disp-pwm
|
||||
- mediatek,mt6595-disp-pwm
|
||||
- mediatek,mt8173-disp-pwm
|
||||
- mediatek,mt8183-disp-pwm
|
||||
- items:
|
||||
- const: mediatek,mt8167-disp-pwm
|
||||
- const: mediatek,mt8173-disp-pwm
|
||||
- items:
|
||||
- enum:
|
||||
- mediatek,mt8186-disp-pwm
|
||||
- mediatek,mt8192-disp-pwm
|
||||
- mediatek,mt8195-disp-pwm
|
||||
- const: mediatek,mt8183-disp-pwm
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
"#pwm-cells":
|
||||
const: 2
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: Main Clock
|
||||
- description: Mm Clock
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: main
|
||||
- const: mm
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- "#pwm-cells"
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/clock/mt8173-clk.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
pwm0: pwm@1401e000 {
|
||||
compatible = "mediatek,mt8173-disp-pwm";
|
||||
reg = <0x1401e000 0x1000>;
|
||||
#pwm-cells = <2>;
|
||||
clocks = <&mmsys CLK_MM_DISP_PWM026M>,
|
||||
<&mmsys CLK_MM_DISP_PWM0MM>;
|
||||
clock-names = "main", "mm";
|
||||
};
|
||||
|
|
@ -3,6 +3,7 @@ MediaTek PWM controller
|
|||
Required properties:
|
||||
- compatible: should be "mediatek,<name>-pwm":
|
||||
- "mediatek,mt2712-pwm": found on mt2712 SoC.
|
||||
- "mediatek,mt6795-pwm": found on mt6795 SoC.
|
||||
- "mediatek,mt7622-pwm": found on mt7622 SoC.
|
||||
- "mediatek,mt7623-pwm": found on mt7623 SoC.
|
||||
- "mediatek,mt7628-pwm": found on mt7628 SoC.
|
||||
|
|
|
|||
|
|
@ -1,45 +0,0 @@
|
|||
MediaTek display PWM controller
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "mediatek,<name>-disp-pwm":
|
||||
- "mediatek,mt2701-disp-pwm": found on mt2701 SoC.
|
||||
- "mediatek,mt6595-disp-pwm": found on mt6595 SoC.
|
||||
- "mediatek,mt8167-disp-pwm", "mediatek,mt8173-disp-pwm": found on mt8167 SoC.
|
||||
- "mediatek,mt8173-disp-pwm": found on mt8173 SoC.
|
||||
- "mediatek,mt8183-disp-pwm": found on mt8183 SoC.$
|
||||
- reg: physical base address and length of the controller's registers.
|
||||
- #pwm-cells: must be 2. See pwm.yaml in this directory for a description of
|
||||
the cell format.
|
||||
- clocks: phandle and clock specifier of the PWM reference clock.
|
||||
- clock-names: must contain the following:
|
||||
- "main": clock used to generate PWM signals.
|
||||
- "mm": sync signals from the modules of mmsys.
|
||||
- pinctrl-names: Must contain a "default" entry.
|
||||
- pinctrl-0: One property must exist for each entry in pinctrl-names.
|
||||
See pinctrl/pinctrl-bindings.txt for details of the property values.
|
||||
|
||||
Example:
|
||||
pwm0: pwm@1401e000 {
|
||||
compatible = "mediatek,mt8173-disp-pwm",
|
||||
"mediatek,mt6595-disp-pwm";
|
||||
reg = <0 0x1401e000 0 0x1000>;
|
||||
#pwm-cells = <2>;
|
||||
clocks = <&mmsys CLK_MM_DISP_PWM026M>,
|
||||
<&mmsys CLK_MM_DISP_PWM0MM>;
|
||||
clock-names = "main", "mm";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&disp_pwm0_pins>;
|
||||
};
|
||||
|
||||
backlight_lcd: backlight_lcd {
|
||||
compatible = "pwm-backlight";
|
||||
pwms = <&pwm0 0 1000000>;
|
||||
brightness-levels = <
|
||||
0 16 32 48 64 80 96 112
|
||||
128 144 160 176 192 208 224 240
|
||||
255
|
||||
>;
|
||||
default-brightness-level = <9>;
|
||||
power-supply = <&mt6397_vio18_reg>;
|
||||
enable-gpios = <&pio 95 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
# Copyright (C) Sunplus Co., Ltd. 2021
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/pwm/sunplus,sp7021-pwm.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Sunplus SoC SP7021 PWM Controller
|
||||
|
||||
maintainers:
|
||||
- Hammer Hsieh <hammerh0314@gmail.com>
|
||||
|
||||
allOf:
|
||||
- $ref: pwm.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: sunplus,sp7021-pwm
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
'#pwm-cells':
|
||||
const: 2
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
required:
|
||||
- reg
|
||||
- clocks
|
||||
|
||||
examples:
|
||||
- |
|
||||
pwm: pwm@9c007a00 {
|
||||
compatible = "sunplus,sp7021-pwm";
|
||||
reg = <0x9c007a00 0x80>;
|
||||
clocks = <&clkc 0xa2>;
|
||||
#pwm-cells = <2>;
|
||||
};
|
||||
|
|
@ -15,14 +15,15 @@ maintainers:
|
|||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- fsl,imx8mq-cm4
|
||||
- fsl,imx6sx-cm4
|
||||
- fsl,imx7d-cm4
|
||||
- fsl,imx7ulp-cm4
|
||||
- fsl,imx8mm-cm4
|
||||
- fsl,imx8mn-cm7
|
||||
- fsl,imx8mp-cm7
|
||||
- fsl,imx8mq-cm4
|
||||
- fsl,imx8ulp-cm33
|
||||
- fsl,imx7d-cm4
|
||||
- fsl,imx7ulp-cm4
|
||||
- fsl,imx6sx-cm4
|
||||
- fsl,imx93-cm33
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
|
|
|||
|
|
@ -23,11 +23,13 @@ properties:
|
|||
|
||||
reg:
|
||||
description:
|
||||
Should contain the address ranges for memory regions SRAM, CFG, and
|
||||
L1TCM.
|
||||
Should contain the address ranges for memory regions SRAM, CFG, and,
|
||||
on some platforms, L1TCM.
|
||||
minItems: 2
|
||||
maxItems: 3
|
||||
|
||||
reg-names:
|
||||
minItems: 2
|
||||
items:
|
||||
- const: sram
|
||||
- const: cfg
|
||||
|
|
@ -42,21 +44,48 @@ properties:
|
|||
clock-names:
|
||||
const: main
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
firmware-name:
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
description:
|
||||
If present, name (or relative path) of the file within the
|
||||
firmware search path containing the firmware image used when
|
||||
initializing SCP.
|
||||
|
||||
memory-region:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- reg-names
|
||||
|
||||
if:
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- mediatek,mt8183-scp
|
||||
- mediatek,mt8192-scp
|
||||
then:
|
||||
required:
|
||||
- clocks
|
||||
- clock-names
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- mediatek,mt8183-scp
|
||||
- mediatek,mt8192-scp
|
||||
then:
|
||||
required:
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- mediatek,mt8183-scp
|
||||
- mediatek,mt8186-scp
|
||||
then:
|
||||
properties:
|
||||
reg:
|
||||
maxItems: 2
|
||||
reg-names:
|
||||
maxItems: 2
|
||||
|
||||
additionalProperties:
|
||||
type: object
|
||||
|
|
@ -76,10 +105,10 @@ additionalProperties:
|
|||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/mt8183-clk.h>
|
||||
#include <dt-bindings/clock/mt8192-clk.h>
|
||||
|
||||
scp@10500000 {
|
||||
compatible = "mediatek,mt8183-scp";
|
||||
compatible = "mediatek,mt8192-scp";
|
||||
reg = <0x10500000 0x80000>,
|
||||
<0x10700000 0x8000>,
|
||||
<0x10720000 0xe0000>;
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ description:
|
|||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- qcom,msm8226-adsp-pil
|
||||
- qcom,msm8974-adsp-pil
|
||||
- qcom,msm8996-adsp-pil
|
||||
- qcom,msm8996-slpi-pil
|
||||
|
|
@ -29,6 +30,9 @@ properties:
|
|||
- qcom,sc8180x-adsp-pas
|
||||
- qcom,sc8180x-cdsp-pas
|
||||
- qcom,sc8180x-mpss-pas
|
||||
- qcom,sc8280xp-adsp-pas
|
||||
- qcom,sc8280xp-nsp0-pas
|
||||
- qcom,sc8280xp-nsp1-pas
|
||||
- qcom,sdm660-adsp-pas
|
||||
- qcom,sdm845-adsp-pas
|
||||
- qcom,sdm845-cdsp-pas
|
||||
|
|
@ -159,6 +163,7 @@ allOf:
|
|||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,msm8226-adsp-pil
|
||||
- qcom,msm8974-adsp-pil
|
||||
- qcom,msm8996-adsp-pil
|
||||
- qcom,msm8996-slpi-pil
|
||||
|
|
@ -169,6 +174,9 @@ allOf:
|
|||
- qcom,sc8180x-adsp-pas
|
||||
- qcom,sc8180x-cdsp-pas
|
||||
- qcom,sc8180x-mpss-pas
|
||||
- qcom,sc8280xp-adsp-pas
|
||||
- qcom,sc8280xp-nsp0-pas
|
||||
- qcom,sc8280xp-nsp1-pas
|
||||
- qcom,sdm845-adsp-pas
|
||||
- qcom,sdm845-cdsp-pas
|
||||
- qcom,sm6350-adsp-pas
|
||||
|
|
@ -274,6 +282,7 @@ allOf:
|
|||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,msm8226-adsp-pil
|
||||
- qcom,msm8974-adsp-pil
|
||||
- qcom,msm8996-adsp-pil
|
||||
- qcom,msm8996-slpi-pil
|
||||
|
|
@ -284,6 +293,9 @@ allOf:
|
|||
- qcom,qcs404-wcss-pas
|
||||
- qcom,sc8180x-adsp-pas
|
||||
- qcom,sc8180x-cdsp-pas
|
||||
- qcom,sc8280xp-adsp-pas
|
||||
- qcom,sc8280xp-nsp0-pas
|
||||
- qcom,sc8280xp-nsp1-pas
|
||||
- qcom,sdm845-adsp-pas
|
||||
- qcom,sdm845-cdsp-pas
|
||||
- qcom,sm6350-adsp-pas
|
||||
|
|
@ -364,6 +376,7 @@ allOf:
|
|||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,msm8226-adsp-pil
|
||||
- qcom,msm8996-adsp-pil
|
||||
- qcom,msm8998-adsp-pas
|
||||
then:
|
||||
|
|
@ -471,6 +484,7 @@ allOf:
|
|||
enum:
|
||||
- qcom,sc8180x-adsp-pas
|
||||
- qcom,sc8180x-cdsp-pas
|
||||
- qcom,sc8280xp-adsp-pas
|
||||
- qcom,sm6350-adsp-pas
|
||||
- qcom,sm8150-slpi-pas
|
||||
- qcom,sm8250-adsp-pas
|
||||
|
|
@ -508,6 +522,22 @@ allOf:
|
|||
- const: cx
|
||||
- const: mxc
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,sc8280xp-nsp0-pas
|
||||
- qcom,sc8280xp-nsp1-pas
|
||||
then:
|
||||
properties:
|
||||
power-domains:
|
||||
items:
|
||||
- description: NSP power domain
|
||||
power-domain-names:
|
||||
items:
|
||||
- const: nsp
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
|
|
@ -546,6 +576,7 @@ allOf:
|
|||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,msm8226-adsp-pil
|
||||
- qcom,msm8974-adsp-pil
|
||||
- qcom,msm8996-adsp-pil
|
||||
- qcom,msm8996-slpi-pil
|
||||
|
|
|
|||
|
|
@ -43,8 +43,8 @@ properties:
|
|||
items:
|
||||
- items:
|
||||
- description: Phandle of syscon block
|
||||
- description: FIXME
|
||||
- description: FIXME
|
||||
- description: The offset of the trust zone setting register
|
||||
- description: The field mask of the trust zone state
|
||||
|
||||
interrupts:
|
||||
description: Should contain the WWDG1 watchdog reset interrupt
|
||||
|
|
@ -101,8 +101,8 @@ properties:
|
|||
items:
|
||||
- items:
|
||||
- description: Phandle of syscon block
|
||||
- description: FIXME
|
||||
- description: FIXME
|
||||
- description: The offset of the power setting register
|
||||
- description: The field mask of the PDDS selection
|
||||
|
||||
st,syscfg-m4-state:
|
||||
$ref: "/schemas/types.yaml#/definitions/phandle-array"
|
||||
|
|
@ -111,8 +111,8 @@ properties:
|
|||
items:
|
||||
- items:
|
||||
- description: Phandle of syscon block with the tamp register
|
||||
- description: FIXME
|
||||
- description: FIXME
|
||||
- description: The offset of the tamp register
|
||||
- description: The field mask of the Cortex-M4 state
|
||||
|
||||
st,syscfg-rsc-tbl:
|
||||
$ref: "/schemas/types.yaml#/definitions/phandle-array"
|
||||
|
|
@ -122,8 +122,8 @@ properties:
|
|||
items:
|
||||
- items:
|
||||
- description: Phandle of syscon block with the tamp register
|
||||
- description: FIXME
|
||||
- description: FIXME
|
||||
- description: The offset of the tamp register
|
||||
- description: The field mask of the Cortex-M4 resource table address
|
||||
|
||||
st,auto-boot:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
Required properties:
|
||||
- compatible: Should one of contain:
|
||||
"nxp,pca85073a",
|
||||
"nxp,pcf85063",
|
||||
"nxp,pcf85063a",
|
||||
"nxp,pcf85063tp",
|
||||
|
|
|
|||
70
Documentation/devicetree/bindings/rtc/renesas,rzn1-rtc.yaml
Normal file
70
Documentation/devicetree/bindings/rtc/renesas,rzn1-rtc.yaml
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/rtc/renesas,rzn1-rtc.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Renesas RZ/N1 SoCs Real-Time Clock DT bindings
|
||||
|
||||
maintainers:
|
||||
- Miquel Raynal <miquel.raynal@bootlin.com>
|
||||
|
||||
allOf:
|
||||
- $ref: rtc.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- renesas,r9a06g032-rtc
|
||||
- const: renesas,rzn1-rtc
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
minItems: 3
|
||||
maxItems: 3
|
||||
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: alarm
|
||||
- const: timer
|
||||
- const: pps
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
clock-names:
|
||||
const: hclk
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- interrupt-names
|
||||
- clocks
|
||||
- clock-names
|
||||
- power-domains
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/clock/r9a06g032-sysctrl.h>
|
||||
rtc@40006000 {
|
||||
compatible = "renesas,r9a06g032-rtc", "renesas,rzn1-rtc";
|
||||
reg = <0x40006000 0x1000>;
|
||||
interrupts = <GIC_SPI 66 IRQ_TYPE_EDGE_RISING>,
|
||||
<GIC_SPI 67 IRQ_TYPE_EDGE_RISING>,
|
||||
<GIC_SPI 68 IRQ_TYPE_EDGE_RISING>;
|
||||
interrupt-names = "alarm", "timer", "pps";
|
||||
clocks = <&sysctrl R9A06G032_HCLK_RTC>;
|
||||
clock-names = "hclk";
|
||||
power-domains = <&sysctrl>;
|
||||
start-year = <2000>;
|
||||
};
|
||||
92
Documentation/devicetree/bindings/timer/xlnx,xps-timer.yaml
Normal file
92
Documentation/devicetree/bindings/timer/xlnx,xps-timer.yaml
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
# SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/timer/xlnx,xps-timer.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Xilinx LogiCORE IP AXI Timer Device Tree Binding
|
||||
|
||||
maintainers:
|
||||
- Sean Anderson <sean.anderson@seco.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: xlnx,xps-timer-1.00.a
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
clock-names:
|
||||
const: s_axi_aclk
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
'#pwm-cells': true
|
||||
|
||||
xlnx,count-width:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
enum: [8, 16, 32]
|
||||
default: 32
|
||||
description:
|
||||
The width of the counter(s), in bits.
|
||||
|
||||
xlnx,one-timer-only:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
enum: [ 0, 1 ]
|
||||
description:
|
||||
Whether only one timer is present in this block.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- xlnx,one-timer-only
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
required:
|
||||
- '#pwm-cells'
|
||||
then:
|
||||
allOf:
|
||||
- required:
|
||||
- clocks
|
||||
- properties:
|
||||
xlnx,one-timer-only:
|
||||
const: 0
|
||||
else:
|
||||
required:
|
||||
- interrupts
|
||||
- if:
|
||||
required:
|
||||
- clocks
|
||||
then:
|
||||
required:
|
||||
- clock-names
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
timer@800e0000 {
|
||||
clock-names = "s_axi_aclk";
|
||||
clocks = <&zynqmp_clk 71>;
|
||||
compatible = "xlnx,xps-timer-1.00.a";
|
||||
reg = <0x800e0000 0x10000>;
|
||||
interrupts = <0 39 2>;
|
||||
xlnx,count-width = <16>;
|
||||
xlnx,one-timer-only = <0x0>;
|
||||
};
|
||||
|
||||
timer@800f0000 {
|
||||
#pwm-cells = <0>;
|
||||
clock-names = "s_axi_aclk";
|
||||
clocks = <&zynqmp_clk 71>;
|
||||
compatible = "xlnx,xps-timer-1.00.a";
|
||||
reg = <0x800e0000 0x10000>;
|
||||
xlnx,count-width = <32>;
|
||||
xlnx,one-timer-only = <0x0>;
|
||||
};
|
||||
|
|
@ -10,6 +10,12 @@ Optional properties:
|
|||
- dlg,use-sw-pm: Add this property to disable the watchdog during suspend.
|
||||
Only use this option if you can't use the watchdog automatic suspend
|
||||
function during a suspend (see register CONTROL_B).
|
||||
- dlg,wdt-sd: Set what happens on watchdog timeout. If this bit is set the
|
||||
watchdog timeout triggers SHUTDOWN, if cleared the watchdog triggers
|
||||
POWERDOWN. Can be 0 or 1. Only use this option if you want to change the
|
||||
default chip's OTP setting for WATCHDOG_SD bit. If this property is NOT
|
||||
set the WATCHDOG_SD bit and on timeout watchdog behavior will match the
|
||||
chip's OTP settings.
|
||||
|
||||
Example: DA9062
|
||||
|
||||
|
|
|
|||
|
|
@ -1,22 +0,0 @@
|
|||
Faraday Technology FTWDT010 watchdog
|
||||
|
||||
This is an IP part from Faraday Technology found in the Gemini
|
||||
SoCs and others.
|
||||
|
||||
Required properties:
|
||||
- compatible : must be one of
|
||||
"faraday,ftwdt010"
|
||||
"cortina,gemini-watchdog", "faraday,ftwdt010"
|
||||
- reg : shall contain base register location and length
|
||||
- interrupts : shall contain the interrupt for the watchdog
|
||||
|
||||
Optional properties:
|
||||
- timeout-sec : the default watchdog timeout in seconds.
|
||||
|
||||
Example:
|
||||
|
||||
watchdog@41000000 {
|
||||
compatible = "faraday,ftwdt010";
|
||||
reg = <0x41000000 0x1000>;
|
||||
interrupts = <3 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/watchdog/faraday,ftwdt010.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Faraday Technology FTWDT010 watchdog
|
||||
|
||||
maintainers:
|
||||
- Linus Walleij <linus.walleij@linaro.org>
|
||||
- Corentin Labbe <clabbe@baylibre.com>
|
||||
|
||||
description: |
|
||||
This is an IP part from Faraday Technology found in the Gemini
|
||||
SoCs and others.
|
||||
|
||||
allOf:
|
||||
- $ref: "watchdog.yaml#"
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- const: faraday,ftwdt010
|
||||
- items:
|
||||
- enum:
|
||||
- cortina,gemini-watchdog
|
||||
- moxa,moxart-watchdog
|
||||
- const: faraday,ftwdt010
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
clock-names:
|
||||
const: PCLK
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
watchdog@41000000 {
|
||||
compatible = "faraday,ftwdt010";
|
||||
reg = <0x41000000 0x1000>;
|
||||
interrupts = <3 IRQ_TYPE_LEVEL_HIGH>;
|
||||
timeout-secs = <5>;
|
||||
};
|
||||
- |
|
||||
watchdog: watchdog@98500000 {
|
||||
compatible = "moxa,moxart-watchdog", "faraday,ftwdt010";
|
||||
reg = <0x98500000 0x10>;
|
||||
clocks = <&clk_apb>;
|
||||
clock-names = "PCLK";
|
||||
};
|
||||
...
|
||||
|
|
@ -19,6 +19,7 @@ properties:
|
|||
- items:
|
||||
- const: fsl,imx8ulp-wdt
|
||||
- const: fsl,imx7ulp-wdt
|
||||
- const: fsl,imx93-wdt
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ Required properties:
|
|||
"mediatek,mt7629-wdt", "mediatek,mt6589-wdt": for MT7629
|
||||
"mediatek,mt7986-wdt", "mediatek,mt6589-wdt": for MT7986
|
||||
"mediatek,mt8183-wdt": for MT8183
|
||||
"mediatek,mt8186-wdt", "mediatek,mt6589-wdt": for MT8186
|
||||
"mediatek,mt8516-wdt", "mediatek,mt6589-wdt": for MT8516
|
||||
"mediatek,mt8192-wdt": for MT8192
|
||||
"mediatek,mt8195-wdt", "mediatek,mt6589-wdt": for MT8195
|
||||
|
|
|
|||
|
|
@ -14,22 +14,29 @@ allOf:
|
|||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- qcom,apss-wdt-qcs404
|
||||
- qcom,apss-wdt-sc7180
|
||||
- qcom,apss-wdt-sc7280
|
||||
- qcom,apss-wdt-sdm845
|
||||
- qcom,apss-wdt-sdx55
|
||||
- qcom,apss-wdt-sm6350
|
||||
- qcom,apss-wdt-sm8150
|
||||
- qcom,apss-wdt-sm8250
|
||||
- qcom,kpss-timer
|
||||
- qcom,kpss-wdt
|
||||
- qcom,kpss-wdt-apq8064
|
||||
- qcom,kpss-wdt-ipq4019
|
||||
- qcom,kpss-wdt-ipq8064
|
||||
- qcom,kpss-wdt-msm8960
|
||||
- qcom,scss-timer
|
||||
oneOf:
|
||||
- items:
|
||||
- enum:
|
||||
- qcom,apss-wdt-qcs404
|
||||
- qcom,apss-wdt-sc7180
|
||||
- qcom,apss-wdt-sc7280
|
||||
- qcom,apss-wdt-sc8180x
|
||||
- qcom,apss-wdt-sc8280xp
|
||||
- qcom,apss-wdt-sdm845
|
||||
- qcom,apss-wdt-sdx55
|
||||
- qcom,apss-wdt-sm6350
|
||||
- qcom,apss-wdt-sm8150
|
||||
- qcom,apss-wdt-sm8250
|
||||
- const: qcom,kpss-wdt
|
||||
- items:
|
||||
- enum:
|
||||
- qcom,kpss-wdt
|
||||
- qcom,kpss-timer
|
||||
- qcom,kpss-wdt-apq8064
|
||||
- qcom,kpss-wdt-ipq4019
|
||||
- qcom,kpss-wdt-ipq8064
|
||||
- qcom,kpss-wdt-msm8960
|
||||
- qcom,scss-timer
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
|
|
|||
|
|
@ -21,8 +21,15 @@ properties:
|
|||
|
||||
- items:
|
||||
- enum:
|
||||
- renesas,r9a06g032-wdt # RZ/N1D
|
||||
- const: renesas,rzn1-wdt # RZ/N1
|
||||
|
||||
- items:
|
||||
- enum:
|
||||
- renesas,r9a07g043-wdt # RZ/G2UL
|
||||
- renesas,r9a07g044-wdt # RZ/G2{L,LC}
|
||||
- const: renesas,rzg2l-wdt # RZ/G2L
|
||||
- renesas,r9a07g054-wdt # RZ/V2L
|
||||
- const: renesas,rzg2l-wdt
|
||||
|
||||
- items:
|
||||
- enum:
|
||||
|
|
@ -52,11 +59,11 @@ properties:
|
|||
- renesas,r8a77980-wdt # R-Car V3H
|
||||
- renesas,r8a77990-wdt # R-Car E3
|
||||
- renesas,r8a77995-wdt # R-Car D3
|
||||
- renesas,r8a779a0-wdt # R-Car V3U
|
||||
- const: renesas,rcar-gen3-wdt # R-Car Gen3 and RZ/G2
|
||||
|
||||
- items:
|
||||
- enum:
|
||||
- renesas,r8a779a0-wdt # R-Car V3U
|
||||
- renesas,r8a779f0-wdt # R-Car S4-8
|
||||
- const: renesas,rcar-gen4-wdt # R-Car Gen4
|
||||
|
||||
|
|
@ -94,6 +101,7 @@ allOf:
|
|||
contains:
|
||||
enum:
|
||||
- renesas,rza-wdt
|
||||
- renesas,rzn1-wdt
|
||||
then:
|
||||
required:
|
||||
- power-domains
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ properties:
|
|||
required:
|
||||
- compatible
|
||||
|
||||
additionalProperties: false
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
|
|
|
|||
|
|
@ -0,0 +1,47 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
# Copyright (C) Sunplus Co., Ltd. 2021
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/watchdog/sunplus,sp7021-wdt.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Sunplus SoCs Watchdog
|
||||
|
||||
maintainers:
|
||||
- XianTao Hu <xt.hu@cqplus1.com>
|
||||
|
||||
allOf:
|
||||
- $ref: watchdog.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: sunplus,sp7021-wdt
|
||||
|
||||
reg:
|
||||
items:
|
||||
- description: watchdog registers regions
|
||||
- description: miscellaneous control registers regions
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- resets
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
watchdog: watchdog@9c000630 {
|
||||
compatible = "sunplus,sp7021-wdt";
|
||||
reg = <0x9c000630 0x08>, <0x9c000274 0x04>;
|
||||
clocks = <&clkc 0x24>;
|
||||
resets = <&rstc 0x14>;
|
||||
};
|
||||
...
|
||||
|
|
@ -49,6 +49,12 @@ After being requested, a PWM has to be configured using::
|
|||
|
||||
This API controls both the PWM period/duty_cycle config and the
|
||||
enable/disable state.
|
||||
|
||||
As a consumer, don't rely on the output's state for a disabled PWM. If it's
|
||||
easily possible, drivers are supposed to emit the inactive state, but some
|
||||
drivers cannot. If you rely on getting the inactive state, use .duty_cycle=0,
|
||||
.enabled=true.
|
||||
|
||||
There is also a usage_power setting: If set, the PWM driver is only required to
|
||||
maintain the power output but has more freedom regarding signal form.
|
||||
If supported by the driver, the signal can be optimized, for example to improve
|
||||
|
|
|
|||
|
|
@ -262,10 +262,10 @@ Translation APIs for Mediated Devices
|
|||
The following APIs are provided for translating user pfn to host pfn in a VFIO
|
||||
driver::
|
||||
|
||||
extern int vfio_pin_pages(struct device *dev, unsigned long *user_pfn,
|
||||
int vfio_pin_pages(struct vfio_device *device, unsigned long *user_pfn,
|
||||
int npage, int prot, unsigned long *phys_pfn);
|
||||
|
||||
extern int vfio_unpin_pages(struct device *dev, unsigned long *user_pfn,
|
||||
int vfio_unpin_pages(struct vfio_device *device, unsigned long *user_pfn,
|
||||
int npage);
|
||||
|
||||
These functions call back into the back-end IOMMU module by using the pin_pages
|
||||
|
|
|
|||
|
|
@ -1,63 +1,82 @@
|
|||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
======================================
|
||||
Enhanced Read-Only File System - EROFS
|
||||
EROFS - Enhanced Read-Only File System
|
||||
======================================
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
EROFS file-system stands for Enhanced Read-Only File System. Different
|
||||
from other read-only file systems, it aims to be designed for flexibility,
|
||||
scalability, but be kept simple and high performance.
|
||||
EROFS filesystem stands for Enhanced Read-Only File System. It aims to form a
|
||||
generic read-only filesystem solution for various read-only use cases instead
|
||||
of just focusing on storage space saving without considering any side effects
|
||||
of runtime performance.
|
||||
|
||||
It is designed as a better filesystem solution for the following scenarios:
|
||||
It is designed to meet the needs of flexibility, feature extendability and user
|
||||
payload friendly, etc. Apart from those, it is still kept as a simple
|
||||
random-access friendly high-performance filesystem to get rid of unneeded I/O
|
||||
amplification and memory-resident overhead compared to similar approaches.
|
||||
|
||||
It is implemented to be a better choice for the following scenarios:
|
||||
|
||||
- read-only storage media or
|
||||
|
||||
- part of a fully trusted read-only solution, which means it needs to be
|
||||
immutable and bit-for-bit identical to the official golden image for
|
||||
their releases due to security and other considerations and
|
||||
their releases due to security or other considerations and
|
||||
|
||||
- hope to minimize extra storage space with guaranteed end-to-end performance
|
||||
by using compact layout, transparent file compression and direct access,
|
||||
especially for those embedded devices with limited memory and high-density
|
||||
hosts with numerous containers;
|
||||
hosts with numerous containers.
|
||||
|
||||
Here is the main features of EROFS:
|
||||
|
||||
- Little endian on-disk design;
|
||||
|
||||
- Currently 4KB block size (nobh) and therefore maximum 16TB address space;
|
||||
- 4KiB block size and 32-bit block addresses, therefore 16TiB address space
|
||||
at most for now;
|
||||
|
||||
- Metadata & data could be mixed by design;
|
||||
- Two inode layouts for different requirements:
|
||||
|
||||
- 2 inode versions for different requirements:
|
||||
|
||||
===================== ============ =====================================
|
||||
===================== ============ ======================================
|
||||
compact (v1) extended (v2)
|
||||
===================== ============ =====================================
|
||||
===================== ============ ======================================
|
||||
Inode metadata size 32 bytes 64 bytes
|
||||
Max file size 4 GB 16 EB (also limited by max. vol size)
|
||||
Max file size 4 GiB 16 EiB (also limited by max. vol size)
|
||||
Max uids/gids 65536 4294967296
|
||||
Per-inode timestamp no yes (64 + 32-bit timestamp)
|
||||
Max hardlinks 65536 4294967296
|
||||
Metadata reserved 4 bytes 14 bytes
|
||||
===================== ============ =====================================
|
||||
Metadata reserved 8 bytes 18 bytes
|
||||
===================== ============ ======================================
|
||||
|
||||
- Metadata and data could be mixed as an option;
|
||||
|
||||
- Support extended attributes (xattrs) as an option;
|
||||
|
||||
- Support xattr inline and tail-end data inline for all files;
|
||||
- Support tailpacking data and xattr inline compared to byte-addressed
|
||||
unaligned metadata or smaller block size alternatives;
|
||||
|
||||
- Support POSIX.1e ACLs by using xattrs;
|
||||
|
||||
- Support transparent data compression as an option:
|
||||
LZ4 algorithm with the fixed-sized output compression for high performance;
|
||||
LZ4 and MicroLZMA algorithms can be used on a per-file basis; In addition,
|
||||
inplace decompression is also supported to avoid bounce compressed buffers
|
||||
and page cache thrashing.
|
||||
|
||||
- Multiple device support for multi-layer container images.
|
||||
- Support direct I/O on uncompressed files to avoid double caching for loop
|
||||
devices;
|
||||
|
||||
- Support FSDAX on uncompressed images for secure containers and ramdisks in
|
||||
order to get rid of unnecessary page cache.
|
||||
|
||||
- Support multiple devices for multi blob container images;
|
||||
|
||||
- Support file-based on-demand loading with the Fscache infrastructure.
|
||||
|
||||
The following git tree provides the file system user-space tools under
|
||||
development (ex, formatting tool mkfs.erofs):
|
||||
development, such as a formatting tool (mkfs.erofs), an on-disk consistency &
|
||||
compatibility checking tool (fsck.erofs), and a debugging tool (dump.erofs):
|
||||
|
||||
- git://git.kernel.org/pub/scm/linux/kernel/git/xiang/erofs-utils.git
|
||||
|
||||
|
|
@ -91,6 +110,7 @@ dax={always,never} Use direct access (no page cache). See
|
|||
Documentation/filesystems/dax.rst.
|
||||
dax A legacy option which is an alias for ``dax=always``.
|
||||
device=%s Specify a path to an extra device to be used together.
|
||||
fsid=%s Specify a filesystem image ID for Fscache back-end.
|
||||
=================== =========================================================
|
||||
|
||||
Sysfs Entries
|
||||
|
|
@ -226,8 +246,8 @@ Note that apart from the offset of the first filename, nameoff0 also indicates
|
|||
the total number of directory entries in this block since it is no need to
|
||||
introduce another on-disk field at all.
|
||||
|
||||
Chunk-based file
|
||||
----------------
|
||||
Chunk-based files
|
||||
-----------------
|
||||
In order to support chunk-based data deduplication, a new inode data layout has
|
||||
been supported since Linux v5.15: Files are split in equal-sized data chunks
|
||||
with ``extents`` area of the inode metadata indicating how to get the chunk
|
||||
|
|
|
|||
216
Documentation/filesystems/nfs/client-identifier.rst
Normal file
216
Documentation/filesystems/nfs/client-identifier.rst
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
=======================
|
||||
NFSv4 client identifier
|
||||
=======================
|
||||
|
||||
This document explains how the NFSv4 protocol identifies client
|
||||
instances in order to maintain file open and lock state during
|
||||
system restarts. A special identifier and principal are maintained
|
||||
on each client. These can be set by administrators, scripts
|
||||
provided by site administrators, or tools provided by Linux
|
||||
distributors.
|
||||
|
||||
There are risks if a client's NFSv4 identifier and its principal
|
||||
are not chosen carefully.
|
||||
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
The NFSv4 protocol uses "lease-based file locking". Leases help
|
||||
NFSv4 servers provide file lock guarantees and manage their
|
||||
resources.
|
||||
|
||||
Simply put, an NFSv4 server creates a lease for each NFSv4 client.
|
||||
The server collects each client's file open and lock state under
|
||||
the lease for that client.
|
||||
|
||||
The client is responsible for periodically renewing its leases.
|
||||
While a lease remains valid, the server holding that lease
|
||||
guarantees the file locks the client has created remain in place.
|
||||
|
||||
If a client stops renewing its lease (for example, if it crashes),
|
||||
the NFSv4 protocol allows the server to remove the client's open
|
||||
and lock state after a certain period of time. When a client
|
||||
restarts, it indicates to servers that open and lock state
|
||||
associated with its previous leases is no longer valid and can be
|
||||
destroyed immediately.
|
||||
|
||||
In addition, each NFSv4 server manages a persistent list of client
|
||||
leases. When the server restarts and clients attempt to recover
|
||||
their state, the server uses this list to distinguish amongst
|
||||
clients that held state before the server restarted and clients
|
||||
sending fresh OPEN and LOCK requests. This enables file locks to
|
||||
persist safely across server restarts.
|
||||
|
||||
NFSv4 client identifiers
|
||||
------------------------
|
||||
|
||||
Each NFSv4 client presents an identifier to NFSv4 servers so that
|
||||
they can associate the client with its lease. Each client's
|
||||
identifier consists of two elements:
|
||||
|
||||
- co_ownerid: An arbitrary but fixed string.
|
||||
|
||||
- boot verifier: A 64-bit incarnation verifier that enables a
|
||||
server to distinguish successive boot epochs of the same client.
|
||||
|
||||
The NFSv4.0 specification refers to these two items as an
|
||||
"nfs_client_id4". The NFSv4.1 specification refers to these two
|
||||
items as a "client_owner4".
|
||||
|
||||
NFSv4 servers tie this identifier to the principal and security
|
||||
flavor that the client used when presenting it. Servers use this
|
||||
principal to authorize subsequent lease modification operations
|
||||
sent by the client. Effectively this principal is a third element of
|
||||
the identifier.
|
||||
|
||||
As part of the identity presented to servers, a good
|
||||
"co_ownerid" string has several important properties:
|
||||
|
||||
- The "co_ownerid" string identifies the client during reboot
|
||||
recovery, therefore the string is persistent across client
|
||||
reboots.
|
||||
- The "co_ownerid" string helps servers distinguish the client
|
||||
from others, therefore the string is globally unique. Note
|
||||
that there is no central authority that assigns "co_ownerid"
|
||||
strings.
|
||||
- Because it often appears on the network in the clear, the
|
||||
"co_ownerid" string does not reveal private information about
|
||||
the client itself.
|
||||
- The content of the "co_ownerid" string is set and unchanging
|
||||
before the client attempts NFSv4 mounts after a restart.
|
||||
- The NFSv4 protocol places a 1024-byte limit on the size of the
|
||||
"co_ownerid" string.
|
||||
|
||||
Protecting NFSv4 lease state
|
||||
----------------------------
|
||||
|
||||
NFSv4 servers utilize the "client_owner4" as described above to
|
||||
assign a unique lease to each client. Under this scheme, there are
|
||||
circumstances where clients can interfere with each other. This is
|
||||
referred to as "lease stealing".
|
||||
|
||||
If distinct clients present the same "co_ownerid" string and use
|
||||
the same principal (for example, AUTH_SYS and UID 0), a server is
|
||||
unable to tell that the clients are not the same. Each distinct
|
||||
client presents a different boot verifier, so it appears to the
|
||||
server as if there is one client that is rebooting frequently.
|
||||
Neither client can maintain open or lock state in this scenario.
|
||||
|
||||
If distinct clients present the same "co_ownerid" string and use
|
||||
distinct principals, the server is likely to allow the first client
|
||||
to operate normally but reject subsequent clients with the same
|
||||
"co_ownerid" string.
|
||||
|
||||
If a client's "co_ownerid" string or principal are not stable,
|
||||
state recovery after a server or client reboot is not guaranteed.
|
||||
If a client unexpectedly restarts but presents a different
|
||||
"co_ownerid" string or principal to the server, the server orphans
|
||||
the client's previous open and lock state. This blocks access to
|
||||
locked files until the server removes the orphaned state.
|
||||
|
||||
If the server restarts and a client presents a changed "co_ownerid"
|
||||
string or principal to the server, the server will not allow the
|
||||
client to reclaim its open and lock state, and may give those locks
|
||||
to other clients in the meantime. This is referred to as "lock
|
||||
stealing".
|
||||
|
||||
Lease stealing and lock stealing increase the potential for denial
|
||||
of service and in rare cases even data corruption.
|
||||
|
||||
Selecting an appropriate client identifier
|
||||
------------------------------------------
|
||||
|
||||
By default, the Linux NFSv4 client implementation constructs its
|
||||
"co_ownerid" string starting with the words "Linux NFS" followed by
|
||||
the client's UTS node name (the same node name, incidentally, that
|
||||
is used as the "machine name" in an AUTH_SYS credential). In small
|
||||
deployments, this construction is usually adequate. Often, however,
|
||||
the node name by itself is not adequately unique, and can change
|
||||
unexpectedly. Problematic situations include:
|
||||
|
||||
- NFS-root (diskless) clients, where the local DCHP server (or
|
||||
equivalent) does not provide a unique host name.
|
||||
|
||||
- "Containers" within a single Linux host. If each container has
|
||||
a separate network namespace, but does not use the UTS namespace
|
||||
to provide a unique host name, then there can be multiple NFS
|
||||
client instances with the same host name.
|
||||
|
||||
- Clients across multiple administrative domains that access a
|
||||
common NFS server. If hostnames are not assigned centrally
|
||||
then uniqueness cannot be guaranteed unless a domain name is
|
||||
included in the hostname.
|
||||
|
||||
Linux provides two mechanisms to add uniqueness to its "co_ownerid"
|
||||
string:
|
||||
|
||||
nfs.nfs4_unique_id
|
||||
This module parameter can set an arbitrary uniquifier string
|
||||
via the kernel command line, or when the "nfs" module is
|
||||
loaded.
|
||||
|
||||
/sys/fs/nfs/client/net/identifier
|
||||
This virtual file, available since Linux 5.3, is local to the
|
||||
network namespace in which it is accessed and so can provide
|
||||
distinction between network namespaces (containers) when the
|
||||
hostname remains uniform.
|
||||
|
||||
Note that this file is empty on name-space creation. If the
|
||||
container system has access to some sort of per-container identity
|
||||
then that uniquifier can be used. For example, a uniquifier might
|
||||
be formed at boot using the container's internal identifier:
|
||||
|
||||
sha256sum /etc/machine-id | awk '{print $1}' \\
|
||||
> /sys/fs/nfs/client/net/identifier
|
||||
|
||||
Security considerations
|
||||
-----------------------
|
||||
|
||||
The use of cryptographic security for lease management operations
|
||||
is strongly encouraged.
|
||||
|
||||
If NFS with Kerberos is not configured, a Linux NFSv4 client uses
|
||||
AUTH_SYS and UID 0 as the principal part of its client identity.
|
||||
This configuration is not only insecure, it increases the risk of
|
||||
lease and lock stealing. However, it might be the only choice for
|
||||
client configurations that have no local persistent storage.
|
||||
"co_ownerid" string uniqueness and persistence is critical in this
|
||||
case.
|
||||
|
||||
When a Kerberos keytab is present on a Linux NFS client, the client
|
||||
attempts to use one of the principals in that keytab when
|
||||
identifying itself to servers. The "sec=" mount option does not
|
||||
control this behavior. Alternately, a single-user client with a
|
||||
Kerberos principal can use that principal in place of the client's
|
||||
host principal.
|
||||
|
||||
Using Kerberos for this purpose enables the client and server to
|
||||
use the same lease for operations covered by all "sec=" settings.
|
||||
Additionally, the Linux NFS client uses the RPCSEC_GSS security
|
||||
flavor with Kerberos and the integrity QOS to prevent in-transit
|
||||
modification of lease modification requests.
|
||||
|
||||
Additional notes
|
||||
----------------
|
||||
The Linux NFSv4 client establishes a single lease on each NFSv4
|
||||
server it accesses. NFSv4 mounts from a Linux NFSv4 client of a
|
||||
particular server then share that lease.
|
||||
|
||||
Once a client establishes open and lock state, the NFSv4 protocol
|
||||
enables lease state to transition to other servers, following data
|
||||
that has been migrated. This hides data migration completely from
|
||||
running applications. The Linux NFSv4 client facilitates state
|
||||
migration by presenting the same "client_owner4" to all servers it
|
||||
encounters.
|
||||
|
||||
========
|
||||
See Also
|
||||
========
|
||||
|
||||
- nfs(5)
|
||||
- kerberos(7)
|
||||
- RFC 7530 for the NFSv4.0 specification
|
||||
- RFC 8881 for the NFSv4.1 specification.
|
||||
|
|
@ -6,6 +6,8 @@ NFS
|
|||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
client-identifier
|
||||
exporting
|
||||
pnfs
|
||||
rpc-cache
|
||||
rpc-server-gss
|
||||
|
|
|
|||
55
MAINTAINERS
55
MAINTAINERS
|
|
@ -382,7 +382,7 @@ F: include/acpi/
|
|||
F: tools/power/acpi/
|
||||
|
||||
ACPI FOR ARM64 (ACPI/arm64)
|
||||
M: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
|
||||
M: Lorenzo Pieralisi <lpieralisi@kernel.org>
|
||||
M: Hanjun Guo <guohanjun@huawei.com>
|
||||
M: Sudeep Holla <sudeep.holla@arm.com>
|
||||
L: linux-acpi@vger.kernel.org
|
||||
|
|
@ -2940,7 +2940,7 @@ N: uniphier
|
|||
ARM/VERSATILE EXPRESS PLATFORM
|
||||
M: Liviu Dudau <liviu.dudau@arm.com>
|
||||
M: Sudeep Holla <sudeep.holla@arm.com>
|
||||
M: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
|
||||
M: Lorenzo Pieralisi <lpieralisi@kernel.org>
|
||||
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
||||
S: Maintained
|
||||
F: */*/*/vexpress*
|
||||
|
|
@ -4566,8 +4566,8 @@ F: drivers/power/supply/cw2015_battery.c
|
|||
|
||||
CEPH COMMON CODE (LIBCEPH)
|
||||
M: Ilya Dryomov <idryomov@gmail.com>
|
||||
M: Jeff Layton <jlayton@kernel.org>
|
||||
M: Xiubo Li <xiubli@redhat.com>
|
||||
R: Jeff Layton <jlayton@kernel.org>
|
||||
L: ceph-devel@vger.kernel.org
|
||||
S: Supported
|
||||
W: http://ceph.com/
|
||||
|
|
@ -4577,9 +4577,9 @@ F: include/linux/crush/
|
|||
F: net/ceph/
|
||||
|
||||
CEPH DISTRIBUTED FILE SYSTEM CLIENT (CEPH)
|
||||
M: Jeff Layton <jlayton@kernel.org>
|
||||
M: Xiubo Li <xiubli@redhat.com>
|
||||
M: Ilya Dryomov <idryomov@gmail.com>
|
||||
R: Jeff Layton <jlayton@kernel.org>
|
||||
L: ceph-devel@vger.kernel.org
|
||||
S: Supported
|
||||
W: http://ceph.com/
|
||||
|
|
@ -5156,7 +5156,7 @@ F: arch/x86/kernel/cpuid.c
|
|||
F: arch/x86/kernel/msr.c
|
||||
|
||||
CPUIDLE DRIVER - ARM BIG LITTLE
|
||||
M: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
|
||||
M: Lorenzo Pieralisi <lpieralisi@kernel.org>
|
||||
M: Daniel Lezcano <daniel.lezcano@linaro.org>
|
||||
L: linux-pm@vger.kernel.org
|
||||
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
||||
|
|
@ -5176,7 +5176,7 @@ F: drivers/cpuidle/cpuidle-exynos.c
|
|||
F: include/linux/platform_data/cpuidle-exynos.h
|
||||
|
||||
CPUIDLE DRIVER - ARM PSCI
|
||||
M: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
|
||||
M: Lorenzo Pieralisi <lpieralisi@kernel.org>
|
||||
M: Sudeep Holla <sudeep.holla@arm.com>
|
||||
L: linux-pm@vger.kernel.org
|
||||
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
||||
|
|
@ -9319,13 +9319,13 @@ F: drivers/i2c/i2c-stub.c
|
|||
I3C DRIVER FOR CADENCE I3C MASTER IP
|
||||
M: Przemysław Gaj <pgaj@cadence.com>
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/i3c/cdns,i3c-master.txt
|
||||
F: Documentation/devicetree/bindings/i3c/cdns,i3c-master.yaml
|
||||
F: drivers/i3c/master/i3c-master-cdns.c
|
||||
|
||||
I3C DRIVER FOR SYNOPSYS DESIGNWARE
|
||||
M: Vitor Soares <vitor.soares@synopsys.com>
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/i3c/snps,dw-i3c-master.txt
|
||||
F: Documentation/devicetree/bindings/i3c/snps,dw-i3c-master.yaml
|
||||
F: drivers/i3c/master/dw*
|
||||
|
||||
I3C SUBSYSTEM
|
||||
|
|
@ -11441,8 +11441,6 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/livepatching/livepatching.g
|
|||
F: Documentation/ABI/testing/sysfs-kernel-livepatch
|
||||
F: Documentation/livepatch/
|
||||
F: arch/powerpc/include/asm/livepatch.h
|
||||
F: arch/s390/include/asm/livepatch.h
|
||||
F: arch/x86/include/asm/livepatch.h
|
||||
F: include/linux/livepatch.h
|
||||
F: kernel/livepatch/
|
||||
F: kernel/module/livepatch.c
|
||||
|
|
@ -13069,7 +13067,7 @@ M: Claudiu Beznea <claudiu.beznea@microchip.com>
|
|||
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
||||
L: linux-pwm@vger.kernel.org
|
||||
S: Supported
|
||||
F: Documentation/devicetree/bindings/pwm/atmel-pwm.txt
|
||||
F: Documentation/devicetree/bindings/pwm/atmel,at91sam-pwm.yaml
|
||||
F: drivers/pwm/pwm-atmel.c
|
||||
|
||||
MICROCHIP SAMA5D2-COMPATIBLE ADC DRIVER
|
||||
|
|
@ -15291,7 +15289,7 @@ F: drivers/pci/controller/pci-v3-semi.c
|
|||
|
||||
PCI ENDPOINT SUBSYSTEM
|
||||
M: Kishon Vijay Abraham I <kishon@ti.com>
|
||||
M: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
|
||||
M: Lorenzo Pieralisi <lpieralisi@kernel.org>
|
||||
R: Krzysztof Wilczyński <kw@linux.com>
|
||||
L: linux-pci@vger.kernel.org
|
||||
S: Supported
|
||||
|
|
@ -15354,7 +15352,7 @@ F: Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
|
|||
F: drivers/pci/controller/pci-xgene-msi.c
|
||||
|
||||
PCI NATIVE HOST BRIDGE AND ENDPOINT DRIVERS
|
||||
M: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
|
||||
M: Lorenzo Pieralisi <lpieralisi@kernel.org>
|
||||
R: Rob Herring <robh@kernel.org>
|
||||
R: Krzysztof Wilczyński <kw@linux.com>
|
||||
L: linux-pci@vger.kernel.org
|
||||
|
|
@ -15907,7 +15905,7 @@ F: include/linux/dtpm.h
|
|||
|
||||
POWER STATE COORDINATION INTERFACE (PSCI)
|
||||
M: Mark Rutland <mark.rutland@arm.com>
|
||||
M: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
|
||||
M: Lorenzo Pieralisi <lpieralisi@kernel.org>
|
||||
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
||||
S: Maintained
|
||||
F: drivers/firmware/psci/
|
||||
|
|
@ -17002,6 +17000,14 @@ S: Supported
|
|||
F: Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
|
||||
F: drivers/iio/adc/rzg2l_adc.c
|
||||
|
||||
RENESAS RZ/N1 RTC CONTROLLER DRIVER
|
||||
M: Miquel Raynal <miquel.raynal@bootlin.com>
|
||||
L: linux-rtc@vger.kernel.org
|
||||
L: linux-renesas-soc@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/rtc/renesas,rzn1-rtc.yaml
|
||||
F: drivers/rtc/rtc-rzn1.c
|
||||
|
||||
RENESAS R-CAR GEN3 & RZ/N1 NAND CONTROLLER DRIVER
|
||||
M: Miquel Raynal <miquel.raynal@bootlin.com>
|
||||
L: linux-mtd@lists.infradead.org
|
||||
|
|
@ -18286,7 +18292,7 @@ F: drivers/net/ethernet/smsc/smc91x.*
|
|||
|
||||
SECURE MONITOR CALL(SMC) CALLING CONVENTION (SMCCC)
|
||||
M: Mark Rutland <mark.rutland@arm.com>
|
||||
M: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
|
||||
M: Lorenzo Pieralisi <lpieralisi@kernel.org>
|
||||
M: Sudeep Holla <sudeep.holla@arm.com>
|
||||
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
||||
S: Maintained
|
||||
|
|
@ -19038,6 +19044,12 @@ S: Maintained
|
|||
F: Documentation/devicetree/bindings/nvmem/sunplus,sp7021-ocotp.yaml
|
||||
F: drivers/nvmem/sunplus-ocotp.c
|
||||
|
||||
SUNPLUS PWM DRIVER
|
||||
M: Hammer Hsieh <hammerh0314@gmail.com>
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/pwm/sunplus,sp7021-pwm.yaml
|
||||
F: drivers/pwm/pwm-sunplus.c
|
||||
|
||||
SUNPLUS RTC DRIVER
|
||||
M: Vincent Shih <vincent.sunplus@gmail.com>
|
||||
L: linux-rtc@vger.kernel.org
|
||||
|
|
@ -19058,6 +19070,13 @@ S: Maintained
|
|||
F: Documentation/devicetree/bindings/serial/sunplus,sp7021-uart.yaml
|
||||
F: drivers/tty/serial/sunplus-uart.c
|
||||
|
||||
SUNPLUS WATCHDOG DRIVER
|
||||
M: Xiantao Hu <xt.hu@cqplus1.com>
|
||||
L: linux-watchdog@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/watchdog/sunplus,sp7021-wdt.yaml
|
||||
F: drivers/watchdog/sunplus_wdt.c
|
||||
|
||||
SUPERH
|
||||
M: Yoshinori Sato <ysato@users.sourceforge.jp>
|
||||
M: Rich Felker <dalias@libc.org>
|
||||
|
|
@ -21827,6 +21846,12 @@ F: drivers/misc/Makefile
|
|||
F: drivers/misc/xilinx_sdfec.c
|
||||
F: include/uapi/misc/xilinx_sdfec.h
|
||||
|
||||
XILINX PWM DRIVER
|
||||
M: Sean Anderson <sean.anderson@seco.com>
|
||||
S: Maintained
|
||||
F: drivers/pwm/pwm-xilinx.c
|
||||
F: include/clocksource/timer-xilinx.h
|
||||
|
||||
XILINX UARTLITE SERIAL DRIVER
|
||||
M: Peter Korsgaard <jacmet@sunsite.dk>
|
||||
L: linux-serial@vger.kernel.org
|
||||
|
|
|
|||
|
|
@ -251,6 +251,10 @@ static int __init xilinx_timer_init(struct device_node *timer)
|
|||
u32 timer_num = 1;
|
||||
int ret;
|
||||
|
||||
/* If this property is present, the device is a PWM and not a timer */
|
||||
if (of_property_read_bool(timer, "#pwm-cells"))
|
||||
return 0;
|
||||
|
||||
if (initialized)
|
||||
return -EINVAL;
|
||||
|
||||
|
|
|
|||
|
|
@ -7,17 +7,9 @@
|
|||
#ifndef _ASM_POWERPC_LIVEPATCH_H
|
||||
#define _ASM_POWERPC_LIVEPATCH_H
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/ftrace.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/sched/task_stack.h>
|
||||
|
||||
#ifdef CONFIG_LIVEPATCH
|
||||
static inline void klp_arch_set_pc(struct ftrace_regs *fregs, unsigned long ip)
|
||||
{
|
||||
ftrace_instruction_pointer_set(fregs, ip);
|
||||
}
|
||||
#endif /* CONFIG_LIVEPATCH */
|
||||
|
||||
#ifdef CONFIG_LIVEPATCH_64
|
||||
static inline void klp_init_thread_info(struct task_struct *p)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -63,7 +63,6 @@
|
|||
#include <asm/machdep.h>
|
||||
#include <asm/udbg.h>
|
||||
#include <asm/smp.h>
|
||||
#include <asm/livepatch.h>
|
||||
#include <asm/hw_irq.h>
|
||||
#include <asm/softirq_stack.h>
|
||||
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@
|
|||
#include <asm/udbg.h>
|
||||
#include <asm/kexec.h>
|
||||
#include <asm/code-patching.h>
|
||||
#include <asm/livepatch.h>
|
||||
#include <asm/ftrace.h>
|
||||
#include <asm/opal.h>
|
||||
#include <asm/cputhreads.h>
|
||||
#include <asm/hw_irq.h>
|
||||
|
|
|
|||
|
|
@ -1,22 +0,0 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* livepatch.h - s390-specific Kernel Live Patching Core
|
||||
*
|
||||
* Copyright (c) 2013-2015 SUSE
|
||||
* Authors: Jiri Kosina
|
||||
* Vojtech Pavlik
|
||||
* Jiri Slaby
|
||||
*/
|
||||
|
||||
#ifndef ASM_LIVEPATCH_H
|
||||
#define ASM_LIVEPATCH_H
|
||||
|
||||
#include <linux/ftrace.h>
|
||||
#include <asm/ptrace.h>
|
||||
|
||||
static inline void klp_arch_set_pc(struct ftrace_regs *fregs, unsigned long ip)
|
||||
{
|
||||
ftrace_instruction_pointer_set(fregs, ip);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* livepatch.h - x86-specific Kernel Live Patching Core
|
||||
*
|
||||
* Copyright (C) 2014 Seth Jennings <sjenning@redhat.com>
|
||||
* Copyright (C) 2014 SUSE
|
||||
*/
|
||||
|
||||
#ifndef _ASM_X86_LIVEPATCH_H
|
||||
#define _ASM_X86_LIVEPATCH_H
|
||||
|
||||
#include <asm/setup.h>
|
||||
#include <linux/ftrace.h>
|
||||
|
||||
static inline void klp_arch_set_pc(struct ftrace_regs *fregs, unsigned long ip)
|
||||
{
|
||||
ftrace_instruction_pointer_set(fregs, ip);
|
||||
}
|
||||
|
||||
#endif /* _ASM_X86_LIVEPATCH_H */
|
||||
|
|
@ -756,24 +756,23 @@ static struct rbd_client *__rbd_get_client(struct rbd_client *rbdc)
|
|||
*/
|
||||
static struct rbd_client *rbd_client_find(struct ceph_options *ceph_opts)
|
||||
{
|
||||
struct rbd_client *client_node;
|
||||
bool found = false;
|
||||
struct rbd_client *rbdc = NULL, *iter;
|
||||
|
||||
if (ceph_opts->flags & CEPH_OPT_NOSHARE)
|
||||
return NULL;
|
||||
|
||||
spin_lock(&rbd_client_list_lock);
|
||||
list_for_each_entry(client_node, &rbd_client_list, node) {
|
||||
if (!ceph_compare_options(ceph_opts, client_node->client)) {
|
||||
__rbd_get_client(client_node);
|
||||
list_for_each_entry(iter, &rbd_client_list, node) {
|
||||
if (!ceph_compare_options(ceph_opts, iter->client)) {
|
||||
__rbd_get_client(iter);
|
||||
|
||||
found = true;
|
||||
rbdc = iter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock(&rbd_client_list_lock);
|
||||
|
||||
return found ? client_node : NULL;
|
||||
return rbdc;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ static int preallocated_oos_pages = 8192;
|
|||
|
||||
static bool intel_gvt_is_valid_gfn(struct intel_vgpu *vgpu, unsigned long gfn)
|
||||
{
|
||||
struct kvm *kvm = vgpu->kvm;
|
||||
struct kvm *kvm = vgpu->vfio_device.kvm;
|
||||
int idx;
|
||||
bool ret;
|
||||
|
||||
|
|
@ -1185,7 +1185,7 @@ static int is_2MB_gtt_possible(struct intel_vgpu *vgpu,
|
|||
|
||||
if (!vgpu->attached)
|
||||
return -EINVAL;
|
||||
pfn = gfn_to_pfn(vgpu->kvm, ops->get_pfn(entry));
|
||||
pfn = gfn_to_pfn(vgpu->vfio_device.kvm, ops->get_pfn(entry));
|
||||
if (is_error_noslot_pfn(pfn))
|
||||
return -EINVAL;
|
||||
return PageTransHuge(pfn_to_page(pfn));
|
||||
|
|
|
|||
|
|
@ -227,11 +227,7 @@ struct intel_vgpu {
|
|||
struct mutex cache_lock;
|
||||
|
||||
struct notifier_block iommu_notifier;
|
||||
struct notifier_block group_notifier;
|
||||
struct kvm *kvm;
|
||||
struct work_struct release_work;
|
||||
atomic_t released;
|
||||
struct vfio_group *vfio_group;
|
||||
|
||||
struct kvm_page_track_notifier_node track_node;
|
||||
#define NR_BKT (1 << 18)
|
||||
|
|
@ -732,7 +728,7 @@ static inline int intel_gvt_read_gpa(struct intel_vgpu *vgpu, unsigned long gpa,
|
|||
{
|
||||
if (!vgpu->attached)
|
||||
return -ESRCH;
|
||||
return vfio_dma_rw(vgpu->vfio_group, gpa, buf, len, false);
|
||||
return vfio_dma_rw(&vgpu->vfio_device, gpa, buf, len, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -750,7 +746,7 @@ static inline int intel_gvt_write_gpa(struct intel_vgpu *vgpu,
|
|||
{
|
||||
if (!vgpu->attached)
|
||||
return -ESRCH;
|
||||
return vfio_dma_rw(vgpu->vfio_group, gpa, buf, len, true);
|
||||
return vfio_dma_rw(&vgpu->vfio_device, gpa, buf, len, true);
|
||||
}
|
||||
|
||||
void intel_gvt_debugfs_remove_vgpu(struct intel_vgpu *vgpu);
|
||||
|
|
|
|||
|
|
@ -228,8 +228,6 @@ static void intel_gvt_cleanup_vgpu_type_groups(struct intel_gvt *gvt)
|
|||
}
|
||||
}
|
||||
|
||||
static void intel_vgpu_release_work(struct work_struct *work);
|
||||
|
||||
static void gvt_unpin_guest_page(struct intel_vgpu *vgpu, unsigned long gfn,
|
||||
unsigned long size)
|
||||
{
|
||||
|
|
@ -243,7 +241,7 @@ static void gvt_unpin_guest_page(struct intel_vgpu *vgpu, unsigned long gfn,
|
|||
for (npage = 0; npage < total_pages; npage++) {
|
||||
unsigned long cur_gfn = gfn + npage;
|
||||
|
||||
ret = vfio_group_unpin_pages(vgpu->vfio_group, &cur_gfn, 1);
|
||||
ret = vfio_unpin_pages(&vgpu->vfio_device, &cur_gfn, 1);
|
||||
drm_WARN_ON(&i915->drm, ret != 1);
|
||||
}
|
||||
}
|
||||
|
|
@ -266,8 +264,8 @@ static int gvt_pin_guest_page(struct intel_vgpu *vgpu, unsigned long gfn,
|
|||
unsigned long cur_gfn = gfn + npage;
|
||||
unsigned long pfn;
|
||||
|
||||
ret = vfio_group_pin_pages(vgpu->vfio_group, &cur_gfn, 1,
|
||||
IOMMU_READ | IOMMU_WRITE, &pfn);
|
||||
ret = vfio_pin_pages(&vgpu->vfio_device, &cur_gfn, 1,
|
||||
IOMMU_READ | IOMMU_WRITE, &pfn);
|
||||
if (ret != 1) {
|
||||
gvt_vgpu_err("vfio_pin_pages failed for gfn 0x%lx, ret %d\n",
|
||||
cur_gfn, ret);
|
||||
|
|
@ -761,23 +759,6 @@ static int intel_vgpu_iommu_notifier(struct notifier_block *nb,
|
|||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static int intel_vgpu_group_notifier(struct notifier_block *nb,
|
||||
unsigned long action, void *data)
|
||||
{
|
||||
struct intel_vgpu *vgpu =
|
||||
container_of(nb, struct intel_vgpu, group_notifier);
|
||||
|
||||
/* the only action we care about */
|
||||
if (action == VFIO_GROUP_NOTIFY_SET_KVM) {
|
||||
vgpu->kvm = data;
|
||||
|
||||
if (!data)
|
||||
schedule_work(&vgpu->release_work);
|
||||
}
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static bool __kvmgt_vgpu_exist(struct intel_vgpu *vgpu)
|
||||
{
|
||||
struct intel_vgpu *itr;
|
||||
|
|
@ -789,7 +770,7 @@ static bool __kvmgt_vgpu_exist(struct intel_vgpu *vgpu)
|
|||
if (!itr->attached)
|
||||
continue;
|
||||
|
||||
if (vgpu->kvm == itr->kvm) {
|
||||
if (vgpu->vfio_device.kvm == itr->vfio_device.kvm) {
|
||||
ret = true;
|
||||
goto out;
|
||||
}
|
||||
|
|
@ -804,61 +785,44 @@ static int intel_vgpu_open_device(struct vfio_device *vfio_dev)
|
|||
struct intel_vgpu *vgpu = vfio_dev_to_vgpu(vfio_dev);
|
||||
unsigned long events;
|
||||
int ret;
|
||||
struct vfio_group *vfio_group;
|
||||
|
||||
vgpu->iommu_notifier.notifier_call = intel_vgpu_iommu_notifier;
|
||||
vgpu->group_notifier.notifier_call = intel_vgpu_group_notifier;
|
||||
|
||||
events = VFIO_IOMMU_NOTIFY_DMA_UNMAP;
|
||||
ret = vfio_register_notifier(vfio_dev->dev, VFIO_IOMMU_NOTIFY, &events,
|
||||
&vgpu->iommu_notifier);
|
||||
ret = vfio_register_notifier(vfio_dev, VFIO_IOMMU_NOTIFY, &events,
|
||||
&vgpu->iommu_notifier);
|
||||
if (ret != 0) {
|
||||
gvt_vgpu_err("vfio_register_notifier for iommu failed: %d\n",
|
||||
ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
events = VFIO_GROUP_NOTIFY_SET_KVM;
|
||||
ret = vfio_register_notifier(vfio_dev->dev, VFIO_GROUP_NOTIFY, &events,
|
||||
&vgpu->group_notifier);
|
||||
if (ret != 0) {
|
||||
gvt_vgpu_err("vfio_register_notifier for group failed: %d\n",
|
||||
ret);
|
||||
ret = -EEXIST;
|
||||
if (vgpu->attached)
|
||||
goto undo_iommu;
|
||||
|
||||
ret = -ESRCH;
|
||||
if (!vgpu->vfio_device.kvm ||
|
||||
vgpu->vfio_device.kvm->mm != current->mm) {
|
||||
gvt_vgpu_err("KVM is required to use Intel vGPU\n");
|
||||
goto undo_iommu;
|
||||
}
|
||||
|
||||
vfio_group =
|
||||
vfio_group_get_external_user_from_dev(vgpu->vfio_device.dev);
|
||||
if (IS_ERR_OR_NULL(vfio_group)) {
|
||||
ret = !vfio_group ? -EFAULT : PTR_ERR(vfio_group);
|
||||
gvt_vgpu_err("vfio_group_get_external_user_from_dev failed\n");
|
||||
goto undo_register;
|
||||
}
|
||||
vgpu->vfio_group = vfio_group;
|
||||
|
||||
ret = -EEXIST;
|
||||
if (vgpu->attached)
|
||||
goto undo_group;
|
||||
|
||||
ret = -ESRCH;
|
||||
if (!vgpu->kvm || vgpu->kvm->mm != current->mm) {
|
||||
gvt_vgpu_err("KVM is required to use Intel vGPU\n");
|
||||
goto undo_group;
|
||||
}
|
||||
kvm_get_kvm(vgpu->vfio_device.kvm);
|
||||
|
||||
ret = -EEXIST;
|
||||
if (__kvmgt_vgpu_exist(vgpu))
|
||||
goto undo_group;
|
||||
goto undo_iommu;
|
||||
|
||||
vgpu->attached = true;
|
||||
kvm_get_kvm(vgpu->kvm);
|
||||
|
||||
kvmgt_protect_table_init(vgpu);
|
||||
gvt_cache_init(vgpu);
|
||||
|
||||
vgpu->track_node.track_write = kvmgt_page_track_write;
|
||||
vgpu->track_node.track_flush_slot = kvmgt_page_track_flush_slot;
|
||||
kvm_page_track_register_notifier(vgpu->kvm, &vgpu->track_node);
|
||||
kvm_page_track_register_notifier(vgpu->vfio_device.kvm,
|
||||
&vgpu->track_node);
|
||||
|
||||
debugfs_create_ulong(KVMGT_DEBUGFS_FILENAME, 0444, vgpu->debugfs,
|
||||
&vgpu->nr_cache_entries);
|
||||
|
|
@ -868,17 +832,9 @@ static int intel_vgpu_open_device(struct vfio_device *vfio_dev)
|
|||
atomic_set(&vgpu->released, 0);
|
||||
return 0;
|
||||
|
||||
undo_group:
|
||||
vfio_group_put_external_user(vgpu->vfio_group);
|
||||
vgpu->vfio_group = NULL;
|
||||
|
||||
undo_register:
|
||||
vfio_unregister_notifier(vfio_dev->dev, VFIO_GROUP_NOTIFY,
|
||||
&vgpu->group_notifier);
|
||||
|
||||
undo_iommu:
|
||||
vfio_unregister_notifier(vfio_dev->dev, VFIO_IOMMU_NOTIFY,
|
||||
&vgpu->iommu_notifier);
|
||||
vfio_unregister_notifier(vfio_dev, VFIO_IOMMU_NOTIFY,
|
||||
&vgpu->iommu_notifier);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -894,8 +850,9 @@ static void intel_vgpu_release_msi_eventfd_ctx(struct intel_vgpu *vgpu)
|
|||
}
|
||||
}
|
||||
|
||||
static void __intel_vgpu_release(struct intel_vgpu *vgpu)
|
||||
static void intel_vgpu_close_device(struct vfio_device *vfio_dev)
|
||||
{
|
||||
struct intel_vgpu *vgpu = vfio_dev_to_vgpu(vfio_dev);
|
||||
struct drm_i915_private *i915 = vgpu->gvt->gt->i915;
|
||||
int ret;
|
||||
|
||||
|
|
@ -907,41 +864,24 @@ static void __intel_vgpu_release(struct intel_vgpu *vgpu)
|
|||
|
||||
intel_gvt_release_vgpu(vgpu);
|
||||
|
||||
ret = vfio_unregister_notifier(vgpu->vfio_device.dev, VFIO_IOMMU_NOTIFY,
|
||||
&vgpu->iommu_notifier);
|
||||
ret = vfio_unregister_notifier(&vgpu->vfio_device, VFIO_IOMMU_NOTIFY,
|
||||
&vgpu->iommu_notifier);
|
||||
drm_WARN(&i915->drm, ret,
|
||||
"vfio_unregister_notifier for iommu failed: %d\n", ret);
|
||||
|
||||
ret = vfio_unregister_notifier(vgpu->vfio_device.dev, VFIO_GROUP_NOTIFY,
|
||||
&vgpu->group_notifier);
|
||||
drm_WARN(&i915->drm, ret,
|
||||
"vfio_unregister_notifier for group failed: %d\n", ret);
|
||||
|
||||
debugfs_remove(debugfs_lookup(KVMGT_DEBUGFS_FILENAME, vgpu->debugfs));
|
||||
|
||||
kvm_page_track_unregister_notifier(vgpu->kvm, &vgpu->track_node);
|
||||
kvm_put_kvm(vgpu->kvm);
|
||||
kvm_page_track_unregister_notifier(vgpu->vfio_device.kvm,
|
||||
&vgpu->track_node);
|
||||
kvmgt_protect_table_destroy(vgpu);
|
||||
gvt_cache_destroy(vgpu);
|
||||
|
||||
intel_vgpu_release_msi_eventfd_ctx(vgpu);
|
||||
vfio_group_put_external_user(vgpu->vfio_group);
|
||||
|
||||
vgpu->kvm = NULL;
|
||||
vgpu->attached = false;
|
||||
}
|
||||
|
||||
static void intel_vgpu_close_device(struct vfio_device *vfio_dev)
|
||||
{
|
||||
__intel_vgpu_release(vfio_dev_to_vgpu(vfio_dev));
|
||||
}
|
||||
|
||||
static void intel_vgpu_release_work(struct work_struct *work)
|
||||
{
|
||||
struct intel_vgpu *vgpu =
|
||||
container_of(work, struct intel_vgpu, release_work);
|
||||
|
||||
__intel_vgpu_release(vgpu);
|
||||
if (vgpu->vfio_device.kvm)
|
||||
kvm_put_kvm(vgpu->vfio_device.kvm);
|
||||
}
|
||||
|
||||
static u64 intel_vgpu_get_bar_addr(struct intel_vgpu *vgpu, int bar)
|
||||
|
|
@ -1690,7 +1630,6 @@ static int intel_vgpu_probe(struct mdev_device *mdev)
|
|||
return PTR_ERR(vgpu);
|
||||
}
|
||||
|
||||
INIT_WORK(&vgpu->release_work, intel_vgpu_release_work);
|
||||
vfio_init_group_dev(&vgpu->vfio_device, &mdev->dev,
|
||||
&intel_vgpu_dev_ops);
|
||||
|
||||
|
|
@ -1728,7 +1667,7 @@ static struct mdev_driver intel_vgpu_mdev_driver = {
|
|||
|
||||
int intel_gvt_page_track_add(struct intel_vgpu *info, u64 gfn)
|
||||
{
|
||||
struct kvm *kvm = info->kvm;
|
||||
struct kvm *kvm = info->vfio_device.kvm;
|
||||
struct kvm_memory_slot *slot;
|
||||
int idx;
|
||||
|
||||
|
|
@ -1758,7 +1697,7 @@ out:
|
|||
|
||||
int intel_gvt_page_track_remove(struct intel_vgpu *info, u64 gfn)
|
||||
{
|
||||
struct kvm *kvm = info->kvm;
|
||||
struct kvm *kvm = info->vfio_device.kvm;
|
||||
struct kvm_memory_slot *slot;
|
||||
int idx;
|
||||
|
||||
|
|
|
|||
|
|
@ -528,6 +528,9 @@ static int ismt_access(struct i2c_adapter *adap, u16 addr,
|
|||
|
||||
case I2C_SMBUS_BLOCK_PROC_CALL:
|
||||
dev_dbg(dev, "I2C_SMBUS_BLOCK_PROC_CALL\n");
|
||||
if (data->block[0] > I2C_SMBUS_BLOCK_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
dma_size = I2C_SMBUS_BLOCK_MAX;
|
||||
desc->tgtaddr_rw = ISMT_DESC_ADDR_RW(addr, 1);
|
||||
desc->wr_len_cmd = data->block[0] + 1;
|
||||
|
|
|
|||
|
|
@ -768,13 +768,8 @@ static int i3c_hci_probe(struct platform_device *pdev)
|
|||
static int i3c_hci_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct i3c_hci *hci = platform_get_drvdata(pdev);
|
||||
int ret;
|
||||
|
||||
ret = i3c_master_unregister(&hci->master);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
return i3c_master_unregister(&hci->master);
|
||||
}
|
||||
|
||||
static const __maybe_unused struct of_device_id i3c_hci_of_match[] = {
|
||||
|
|
|
|||
|
|
@ -1597,12 +1597,11 @@ static int __maybe_unused svc_i3c_runtime_suspend(struct device *dev)
|
|||
static int __maybe_unused svc_i3c_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct svc_i3c_master *master = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
|
||||
pinctrl_pm_select_default_state(dev);
|
||||
svc_i3c_master_prepare_clks(master);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops svc_i3c_pm_ops = {
|
||||
|
|
|
|||
|
|
@ -1005,7 +1005,7 @@ bool dm_table_request_based(struct dm_table *t)
|
|||
return __table_type_request_based(dm_table_get_type(t));
|
||||
}
|
||||
|
||||
static int dm_table_supports_poll(struct dm_table *t);
|
||||
static bool dm_table_supports_poll(struct dm_table *t);
|
||||
|
||||
static int dm_table_alloc_md_mempools(struct dm_table *t, struct mapped_device *md)
|
||||
{
|
||||
|
|
@ -1027,7 +1027,7 @@ static int dm_table_alloc_md_mempools(struct dm_table *t, struct mapped_device *
|
|||
per_io_data_size = max(per_io_data_size, ti->per_io_data_size);
|
||||
min_pool_size = max(min_pool_size, ti->num_flush_bios);
|
||||
}
|
||||
poll_supported = !!dm_table_supports_poll(t);
|
||||
poll_supported = dm_table_supports_poll(t);
|
||||
}
|
||||
|
||||
t->mempools = dm_alloc_md_mempools(md, type, per_io_data_size, min_pool_size,
|
||||
|
|
@ -1613,9 +1613,20 @@ static int count_device(struct dm_target *ti, struct dm_dev *dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int dm_table_supports_poll(struct dm_table *t)
|
||||
static bool dm_table_supports_poll(struct dm_table *t)
|
||||
{
|
||||
return !dm_table_any_dev_attr(t, device_not_poll_capable, NULL);
|
||||
struct dm_target *ti;
|
||||
unsigned i = 0;
|
||||
|
||||
while (i < dm_table_get_num_targets(t)) {
|
||||
ti = dm_table_get_target(t, i++);
|
||||
|
||||
if (!ti->type->iterate_devices ||
|
||||
ti->type->iterate_devices(ti, device_not_poll_capable, NULL))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -1312,6 +1312,7 @@ bad:
|
|||
|
||||
static struct target_type verity_target = {
|
||||
.name = "verity",
|
||||
.features = DM_TARGET_IMMUTABLE,
|
||||
.version = {1, 8, 0},
|
||||
.module = THIS_MODULE,
|
||||
.ctr = verity_ctr,
|
||||
|
|
|
|||
|
|
@ -87,6 +87,11 @@ static int mlx5_device_enable_sriov(struct mlx5_core_dev *dev, int num_vfs)
|
|||
enable_vfs_hca:
|
||||
num_msix_count = mlx5_get_default_msix_vec_count(dev, num_vfs);
|
||||
for (vf = 0; vf < num_vfs; vf++) {
|
||||
/* Notify the VF before its enablement to let it set
|
||||
* some stuff.
|
||||
*/
|
||||
blocking_notifier_call_chain(&sriov->vfs_ctx[vf].notifier,
|
||||
MLX5_PF_NOTIFY_ENABLE_VF, dev);
|
||||
err = mlx5_core_enable_hca(dev, vf + 1);
|
||||
if (err) {
|
||||
mlx5_core_warn(dev, "failed to enable VF %d (%d)\n", vf, err);
|
||||
|
|
@ -127,6 +132,11 @@ mlx5_device_disable_sriov(struct mlx5_core_dev *dev, int num_vfs, bool clear_vf)
|
|||
for (vf = num_vfs - 1; vf >= 0; vf--) {
|
||||
if (!sriov->vfs_ctx[vf].enabled)
|
||||
continue;
|
||||
/* Notify the VF before its disablement to let it clean
|
||||
* some resources.
|
||||
*/
|
||||
blocking_notifier_call_chain(&sriov->vfs_ctx[vf].notifier,
|
||||
MLX5_PF_NOTIFY_DISABLE_VF, dev);
|
||||
err = mlx5_core_disable_hca(dev, vf + 1);
|
||||
if (err) {
|
||||
mlx5_core_warn(dev, "failed to disable VF %d\n", vf);
|
||||
|
|
@ -257,7 +267,7 @@ int mlx5_sriov_init(struct mlx5_core_dev *dev)
|
|||
{
|
||||
struct mlx5_core_sriov *sriov = &dev->priv.sriov;
|
||||
struct pci_dev *pdev = dev->pdev;
|
||||
int total_vfs;
|
||||
int total_vfs, i;
|
||||
|
||||
if (!mlx5_core_is_pf(dev))
|
||||
return 0;
|
||||
|
|
@ -269,6 +279,9 @@ int mlx5_sriov_init(struct mlx5_core_dev *dev)
|
|||
if (!sriov->vfs_ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < total_vfs; i++)
|
||||
BLOCKING_INIT_NOTIFIER_HEAD(&sriov->vfs_ctx[i].notifier);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -281,3 +294,53 @@ void mlx5_sriov_cleanup(struct mlx5_core_dev *dev)
|
|||
|
||||
kfree(sriov->vfs_ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* mlx5_sriov_blocking_notifier_unregister - Unregister a VF from
|
||||
* a notification block chain.
|
||||
*
|
||||
* @mdev: The mlx5 core device.
|
||||
* @vf_id: The VF id.
|
||||
* @nb: The notifier block to be unregistered.
|
||||
*/
|
||||
void mlx5_sriov_blocking_notifier_unregister(struct mlx5_core_dev *mdev,
|
||||
int vf_id,
|
||||
struct notifier_block *nb)
|
||||
{
|
||||
struct mlx5_vf_context *vfs_ctx;
|
||||
struct mlx5_core_sriov *sriov;
|
||||
|
||||
sriov = &mdev->priv.sriov;
|
||||
if (WARN_ON(vf_id < 0 || vf_id >= sriov->num_vfs))
|
||||
return;
|
||||
|
||||
vfs_ctx = &sriov->vfs_ctx[vf_id];
|
||||
blocking_notifier_chain_unregister(&vfs_ctx->notifier, nb);
|
||||
}
|
||||
EXPORT_SYMBOL(mlx5_sriov_blocking_notifier_unregister);
|
||||
|
||||
/**
|
||||
* mlx5_sriov_blocking_notifier_register - Register a VF notification
|
||||
* block chain.
|
||||
*
|
||||
* @mdev: The mlx5 core device.
|
||||
* @vf_id: The VF id.
|
||||
* @nb: The notifier block to be called upon the VF events.
|
||||
*
|
||||
* Returns 0 on success or an error code.
|
||||
*/
|
||||
int mlx5_sriov_blocking_notifier_register(struct mlx5_core_dev *mdev,
|
||||
int vf_id,
|
||||
struct notifier_block *nb)
|
||||
{
|
||||
struct mlx5_vf_context *vfs_ctx;
|
||||
struct mlx5_core_sriov *sriov;
|
||||
|
||||
sriov = &mdev->priv.sriov;
|
||||
if (vf_id < 0 || vf_id >= sriov->num_vfs)
|
||||
return -EINVAL;
|
||||
|
||||
vfs_ctx = &sriov->vfs_ctx[vf_id];
|
||||
return blocking_notifier_chain_register(&vfs_ctx->notifier, nb);
|
||||
}
|
||||
EXPORT_SYMBOL(mlx5_sriov_blocking_notifier_register);
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@
|
|||
#include <linux/pci.h>
|
||||
#include <linux/pci-ecam.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/slab.h>
|
||||
|
|
@ -196,8 +195,6 @@ static inline void brcm_pcie_bridge_sw_init_set_generic(struct brcm_pcie *pcie,
|
|||
static inline void brcm_pcie_perst_set_4908(struct brcm_pcie *pcie, u32 val);
|
||||
static inline void brcm_pcie_perst_set_7278(struct brcm_pcie *pcie, u32 val);
|
||||
static inline void brcm_pcie_perst_set_generic(struct brcm_pcie *pcie, u32 val);
|
||||
static int brcm_pcie_linkup(struct brcm_pcie *pcie);
|
||||
static int brcm_pcie_add_bus(struct pci_bus *bus);
|
||||
|
||||
enum {
|
||||
RGR1_SW_INIT_1,
|
||||
|
|
@ -286,14 +283,6 @@ static const struct pcie_cfg_data bcm2711_cfg = {
|
|||
.bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic,
|
||||
};
|
||||
|
||||
struct subdev_regulators {
|
||||
unsigned int num_supplies;
|
||||
struct regulator_bulk_data supplies[];
|
||||
};
|
||||
|
||||
static int pci_subdev_regulators_add_bus(struct pci_bus *bus);
|
||||
static void pci_subdev_regulators_remove_bus(struct pci_bus *bus);
|
||||
|
||||
struct brcm_msi {
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
|
|
@ -331,9 +320,6 @@ struct brcm_pcie {
|
|||
u32 hw_rev;
|
||||
void (*perst_set)(struct brcm_pcie *pcie, u32 val);
|
||||
void (*bridge_sw_init_set)(struct brcm_pcie *pcie, u32 val);
|
||||
bool refusal_mode;
|
||||
struct subdev_regulators *sr;
|
||||
bool ep_wakeup_capable;
|
||||
};
|
||||
|
||||
static inline bool is_bmips(const struct brcm_pcie *pcie)
|
||||
|
|
@ -450,99 +436,6 @@ static int brcm_pcie_set_ssc(struct brcm_pcie *pcie)
|
|||
return ssc && pll ? 0 : -EIO;
|
||||
}
|
||||
|
||||
static void *alloc_subdev_regulators(struct device *dev)
|
||||
{
|
||||
static const char * const supplies[] = {
|
||||
"vpcie3v3",
|
||||
"vpcie3v3aux",
|
||||
"vpcie12v",
|
||||
};
|
||||
const size_t size = sizeof(struct subdev_regulators)
|
||||
+ sizeof(struct regulator_bulk_data) * ARRAY_SIZE(supplies);
|
||||
struct subdev_regulators *sr;
|
||||
int i;
|
||||
|
||||
sr = devm_kzalloc(dev, size, GFP_KERNEL);
|
||||
if (sr) {
|
||||
sr->num_supplies = ARRAY_SIZE(supplies);
|
||||
for (i = 0; i < ARRAY_SIZE(supplies); i++)
|
||||
sr->supplies[i].supply = supplies[i];
|
||||
}
|
||||
|
||||
return sr;
|
||||
}
|
||||
|
||||
static int pci_subdev_regulators_add_bus(struct pci_bus *bus)
|
||||
{
|
||||
struct device *dev = &bus->dev;
|
||||
struct subdev_regulators *sr;
|
||||
int ret;
|
||||
|
||||
if (!dev->of_node || !bus->parent || !pci_is_root_bus(bus->parent))
|
||||
return 0;
|
||||
|
||||
if (dev->driver_data)
|
||||
dev_err(dev, "dev.driver_data unexpectedly non-NULL\n");
|
||||
|
||||
sr = alloc_subdev_regulators(dev);
|
||||
if (!sr)
|
||||
return -ENOMEM;
|
||||
|
||||
dev->driver_data = sr;
|
||||
ret = regulator_bulk_get(dev, sr->num_supplies, sr->supplies);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regulator_bulk_enable(sr->num_supplies, sr->supplies);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable regulators for downstream device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int brcm_pcie_add_bus(struct pci_bus *bus)
|
||||
{
|
||||
struct device *dev = &bus->dev;
|
||||
struct brcm_pcie *pcie = (struct brcm_pcie *) bus->sysdata;
|
||||
int ret;
|
||||
|
||||
if (!dev->of_node || !bus->parent || !pci_is_root_bus(bus->parent))
|
||||
return 0;
|
||||
|
||||
ret = pci_subdev_regulators_add_bus(bus);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Grab the regulators for suspend/resume */
|
||||
pcie->sr = bus->dev.driver_data;
|
||||
|
||||
/*
|
||||
* If we have failed linkup there is no point to return an error as
|
||||
* currently it will cause a WARNING() from pci_alloc_child_bus().
|
||||
* We return 0 and turn on the "refusal_mode" so that any further
|
||||
* accesses to the pci_dev just get 0xffffffff
|
||||
*/
|
||||
if (brcm_pcie_linkup(pcie) != 0)
|
||||
pcie->refusal_mode = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pci_subdev_regulators_remove_bus(struct pci_bus *bus)
|
||||
{
|
||||
struct device *dev = &bus->dev;
|
||||
struct subdev_regulators *sr = dev->driver_data;
|
||||
|
||||
if (!sr || !bus->parent || !pci_is_root_bus(bus->parent))
|
||||
return;
|
||||
|
||||
if (regulator_bulk_disable(sr->num_supplies, sr->supplies))
|
||||
dev_err(dev, "failed to disable regulators for downstream device\n");
|
||||
dev->driver_data = NULL;
|
||||
}
|
||||
|
||||
/* Limits operation to a specific generation (1, 2, or 3) */
|
||||
static void brcm_pcie_set_gen(struct brcm_pcie *pcie, int gen)
|
||||
{
|
||||
|
|
@ -858,18 +751,6 @@ static void __iomem *brcm_pcie_map_conf(struct pci_bus *bus, unsigned int devfn,
|
|||
/* Accesses to the RC go right to the RC registers if slot==0 */
|
||||
if (pci_is_root_bus(bus))
|
||||
return PCI_SLOT(devfn) ? NULL : base + where;
|
||||
if (pcie->refusal_mode) {
|
||||
/*
|
||||
* At this point we do not have link. There will be a CPU
|
||||
* abort -- a quirk with this controller --if Linux tries
|
||||
* to read any config-space registers besides those
|
||||
* targeting the host bridge. To prevent this we hijack
|
||||
* the address to point to a safe access that will return
|
||||
* 0xffffffff.
|
||||
*/
|
||||
writel(0xffffffff, base + PCIE_MISC_RC_BAR2_CONFIG_HI);
|
||||
return base + PCIE_MISC_RC_BAR2_CONFIG_HI + (where & 0x3);
|
||||
}
|
||||
|
||||
/* For devices, write to the config space index register */
|
||||
idx = PCIE_ECAM_OFFSET(bus->number, devfn, 0);
|
||||
|
|
@ -898,8 +779,6 @@ static struct pci_ops brcm_pcie_ops = {
|
|||
.map_bus = brcm_pcie_map_conf,
|
||||
.read = pci_generic_config_read,
|
||||
.write = pci_generic_config_write,
|
||||
.add_bus = brcm_pcie_add_bus,
|
||||
.remove_bus = pci_subdev_regulators_remove_bus,
|
||||
};
|
||||
|
||||
static struct pci_ops brcm_pcie_ops32 = {
|
||||
|
|
@ -1047,9 +926,16 @@ static inline int brcm_pcie_get_rc_bar2_size_and_offset(struct brcm_pcie *pcie,
|
|||
|
||||
static int brcm_pcie_setup(struct brcm_pcie *pcie)
|
||||
{
|
||||
struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie);
|
||||
u64 rc_bar2_offset, rc_bar2_size;
|
||||
void __iomem *base = pcie->base;
|
||||
int ret, memc;
|
||||
struct device *dev = pcie->dev;
|
||||
struct resource_entry *entry;
|
||||
bool ssc_good = false;
|
||||
struct resource *res;
|
||||
int num_out_wins = 0;
|
||||
u16 nlw, cls, lnksta;
|
||||
int i, ret, memc;
|
||||
u32 tmp, burst, aspm_support;
|
||||
|
||||
/* Reset the bridge */
|
||||
|
|
@ -1139,40 +1025,6 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie)
|
|||
if (pcie->gen)
|
||||
brcm_pcie_set_gen(pcie, pcie->gen);
|
||||
|
||||
/* Don't advertise L0s capability if 'aspm-no-l0s' */
|
||||
aspm_support = PCIE_LINK_STATE_L1;
|
||||
if (!of_property_read_bool(pcie->np, "aspm-no-l0s"))
|
||||
aspm_support |= PCIE_LINK_STATE_L0S;
|
||||
tmp = readl(base + PCIE_RC_CFG_PRIV1_LINK_CAPABILITY);
|
||||
u32p_replace_bits(&tmp, aspm_support,
|
||||
PCIE_RC_CFG_PRIV1_LINK_CAPABILITY_ASPM_SUPPORT_MASK);
|
||||
writel(tmp, base + PCIE_RC_CFG_PRIV1_LINK_CAPABILITY);
|
||||
|
||||
/*
|
||||
* For config space accesses on the RC, show the right class for
|
||||
* a PCIe-PCIe bridge (the default setting is to be EP mode).
|
||||
*/
|
||||
tmp = readl(base + PCIE_RC_CFG_PRIV1_ID_VAL3);
|
||||
u32p_replace_bits(&tmp, 0x060400,
|
||||
PCIE_RC_CFG_PRIV1_ID_VAL3_CLASS_CODE_MASK);
|
||||
writel(tmp, base + PCIE_RC_CFG_PRIV1_ID_VAL3);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int brcm_pcie_linkup(struct brcm_pcie *pcie)
|
||||
{
|
||||
struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie);
|
||||
struct device *dev = pcie->dev;
|
||||
void __iomem *base = pcie->base;
|
||||
struct resource_entry *entry;
|
||||
struct resource *res;
|
||||
int num_out_wins = 0;
|
||||
u16 nlw, cls, lnksta;
|
||||
bool ssc_good = false;
|
||||
u32 tmp;
|
||||
int ret, i;
|
||||
|
||||
/* Unassert the fundamental reset */
|
||||
pcie->perst_set(pcie, 0);
|
||||
|
||||
|
|
@ -1223,6 +1075,24 @@ static int brcm_pcie_linkup(struct brcm_pcie *pcie)
|
|||
num_out_wins++;
|
||||
}
|
||||
|
||||
/* Don't advertise L0s capability if 'aspm-no-l0s' */
|
||||
aspm_support = PCIE_LINK_STATE_L1;
|
||||
if (!of_property_read_bool(pcie->np, "aspm-no-l0s"))
|
||||
aspm_support |= PCIE_LINK_STATE_L0S;
|
||||
tmp = readl(base + PCIE_RC_CFG_PRIV1_LINK_CAPABILITY);
|
||||
u32p_replace_bits(&tmp, aspm_support,
|
||||
PCIE_RC_CFG_PRIV1_LINK_CAPABILITY_ASPM_SUPPORT_MASK);
|
||||
writel(tmp, base + PCIE_RC_CFG_PRIV1_LINK_CAPABILITY);
|
||||
|
||||
/*
|
||||
* For config space accesses on the RC, show the right class for
|
||||
* a PCIe-PCIe bridge (the default setting is to be EP mode).
|
||||
*/
|
||||
tmp = readl(base + PCIE_RC_CFG_PRIV1_ID_VAL3);
|
||||
u32p_replace_bits(&tmp, 0x060400,
|
||||
PCIE_RC_CFG_PRIV1_ID_VAL3_CLASS_CODE_MASK);
|
||||
writel(tmp, base + PCIE_RC_CFG_PRIV1_ID_VAL3);
|
||||
|
||||
if (pcie->ssc) {
|
||||
ret = brcm_pcie_set_ssc(pcie);
|
||||
if (ret == 0)
|
||||
|
|
@ -1351,21 +1221,9 @@ static void brcm_pcie_turn_off(struct brcm_pcie *pcie)
|
|||
pcie->bridge_sw_init_set(pcie, 1);
|
||||
}
|
||||
|
||||
static int pci_dev_may_wakeup(struct pci_dev *dev, void *data)
|
||||
{
|
||||
bool *ret = data;
|
||||
|
||||
if (device_may_wakeup(&dev->dev)) {
|
||||
*ret = true;
|
||||
dev_info(&dev->dev, "disable cancelled for wake-up device\n");
|
||||
}
|
||||
return (int) *ret;
|
||||
}
|
||||
|
||||
static int brcm_pcie_suspend(struct device *dev)
|
||||
{
|
||||
struct brcm_pcie *pcie = dev_get_drvdata(dev);
|
||||
struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie);
|
||||
int ret;
|
||||
|
||||
brcm_pcie_turn_off(pcie);
|
||||
|
|
@ -1383,25 +1241,6 @@ static int brcm_pcie_suspend(struct device *dev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (pcie->sr) {
|
||||
/*
|
||||
* Now turn off the regulators, but if at least one
|
||||
* downstream device is enabled as a wake-up source, do not
|
||||
* turn off regulators.
|
||||
*/
|
||||
pcie->ep_wakeup_capable = false;
|
||||
pci_walk_bus(bridge->bus, pci_dev_may_wakeup,
|
||||
&pcie->ep_wakeup_capable);
|
||||
if (!pcie->ep_wakeup_capable) {
|
||||
ret = regulator_bulk_disable(pcie->sr->num_supplies,
|
||||
pcie->sr->supplies);
|
||||
if (ret) {
|
||||
dev_err(dev, "Could not turn off regulators\n");
|
||||
reset_control_reset(pcie->rescal);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
clk_disable_unprepare(pcie->clk);
|
||||
|
||||
return 0;
|
||||
|
|
@ -1419,28 +1258,9 @@ static int brcm_pcie_resume(struct device *dev)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (pcie->sr) {
|
||||
if (pcie->ep_wakeup_capable) {
|
||||
/*
|
||||
* We are resuming from a suspend. In the suspend we
|
||||
* did not disable the power supplies, so there is
|
||||
* no need to enable them (and falsely increase their
|
||||
* usage count).
|
||||
*/
|
||||
pcie->ep_wakeup_capable = false;
|
||||
} else {
|
||||
ret = regulator_bulk_enable(pcie->sr->num_supplies,
|
||||
pcie->sr->supplies);
|
||||
if (ret) {
|
||||
dev_err(dev, "Could not turn on regulators\n");
|
||||
goto err_disable_clk;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = reset_control_reset(pcie->rescal);
|
||||
if (ret)
|
||||
goto err_regulator;
|
||||
goto err_disable_clk;
|
||||
|
||||
ret = brcm_phy_start(pcie);
|
||||
if (ret)
|
||||
|
|
@ -1461,10 +1281,6 @@ static int brcm_pcie_resume(struct device *dev)
|
|||
if (ret)
|
||||
goto err_reset;
|
||||
|
||||
ret = brcm_pcie_linkup(pcie);
|
||||
if (ret)
|
||||
goto err_reset;
|
||||
|
||||
if (pcie->msi)
|
||||
brcm_msi_set_regs(pcie->msi);
|
||||
|
||||
|
|
@ -1472,9 +1288,6 @@ static int brcm_pcie_resume(struct device *dev)
|
|||
|
||||
err_reset:
|
||||
reset_control_rearm(pcie->rescal);
|
||||
err_regulator:
|
||||
if (pcie->sr)
|
||||
regulator_bulk_disable(pcie->sr->num_supplies, pcie->sr->supplies);
|
||||
err_disable_clk:
|
||||
clk_disable_unprepare(pcie->clk);
|
||||
return ret;
|
||||
|
|
@ -1606,17 +1419,7 @@ static int brcm_pcie_probe(struct platform_device *pdev)
|
|||
|
||||
platform_set_drvdata(pdev, pcie);
|
||||
|
||||
ret = pci_host_probe(bridge);
|
||||
if (!ret && !brcm_pcie_link_up(pcie))
|
||||
ret = -ENODEV;
|
||||
|
||||
if (ret) {
|
||||
brcm_pcie_remove(pdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
return pci_host_probe(bridge);
|
||||
fail:
|
||||
__brcm_pcie_remove(pcie);
|
||||
return ret;
|
||||
|
|
@ -1625,8 +1428,8 @@ fail:
|
|||
MODULE_DEVICE_TABLE(of, brcm_pcie_match);
|
||||
|
||||
static const struct dev_pm_ops brcm_pcie_pm_ops = {
|
||||
.suspend_noirq = brcm_pcie_suspend,
|
||||
.resume_noirq = brcm_pcie_resume,
|
||||
.suspend = brcm_pcie_suspend,
|
||||
.resume = brcm_pcie_resume,
|
||||
};
|
||||
|
||||
static struct platform_driver brcm_pcie_driver = {
|
||||
|
|
|
|||
|
|
@ -2967,6 +2967,8 @@ static const struct dmi_system_id bridge_d3_blacklist[] = {
|
|||
DMI_MATCH(DMI_BOARD_VENDOR, "Gigabyte Technology Co., Ltd."),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "X299 DESIGNARE EX-CF"),
|
||||
},
|
||||
},
|
||||
{
|
||||
/*
|
||||
* Downstream device is not accessible after putting a root port
|
||||
* into D3cold and back into D0 on Elo i2.
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ config TCIC
|
|||
|
||||
config PCMCIA_ALCHEMY_DEVBOARD
|
||||
tristate "Alchemy Db/Pb1xxx PCMCIA socket services"
|
||||
depends on MIPS_ALCHEMY && PCMCIA
|
||||
depends on MIPS_DB1XXX && PCMCIA
|
||||
help
|
||||
Enable this driver of you want PCMCIA support on your Alchemy
|
||||
Db1000, Db/Pb1100, Db/Pb1500, Db/Pb1550, Db/Pb1200, DB1300
|
||||
|
|
|
|||
|
|
@ -327,10 +327,11 @@ static int bcm63xx_drv_pcmcia_probe(struct platform_device *pdev)
|
|||
{
|
||||
struct bcm63xx_pcmcia_socket *skt;
|
||||
struct pcmcia_socket *sock;
|
||||
struct resource *res, *irq_res;
|
||||
struct resource *res;
|
||||
unsigned int regmem_size = 0, iomem_size = 0;
|
||||
u32 val;
|
||||
int ret;
|
||||
int irq;
|
||||
|
||||
skt = kzalloc(sizeof(*skt), GFP_KERNEL);
|
||||
if (!skt)
|
||||
|
|
@ -342,9 +343,9 @@ static int bcm63xx_drv_pcmcia_probe(struct platform_device *pdev)
|
|||
/* make sure we have all resources we need */
|
||||
skt->common_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
skt->attr_res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
|
||||
irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
skt->pd = pdev->dev.platform_data;
|
||||
if (!skt->common_res || !skt->attr_res || !irq_res || !skt->pd) {
|
||||
if (!skt->common_res || !skt->attr_res || (irq < 0) || !skt->pd) {
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
|
@ -380,7 +381,7 @@ static int bcm63xx_drv_pcmcia_probe(struct platform_device *pdev)
|
|||
sock->dev.parent = &pdev->dev;
|
||||
sock->features = SS_CAP_STATIC_MAP | SS_CAP_PCCARD;
|
||||
sock->io_offset = (unsigned long)skt->io_base;
|
||||
sock->pci_irq = irq_res->start;
|
||||
sock->pci_irq = irq;
|
||||
|
||||
#ifdef CONFIG_CARDBUS
|
||||
sock->cb_dev = bcm63xx_cb_dev;
|
||||
|
|
|
|||
|
|
@ -394,7 +394,7 @@ static int do_validate_mem(struct pcmcia_socket *s,
|
|||
* do_mem_probe() checks a memory region for use by the PCMCIA subsystem.
|
||||
* To do so, the area is split up into sensible parts, and then passed
|
||||
* into the @validate() function. Only if @validate() and @fallback() fail,
|
||||
* the area is marked as unavaibale for use by the PCMCIA subsystem. The
|
||||
* the area is marked as unavailable for use by the PCMCIA subsystem. The
|
||||
* function returns the size of the usable memory area.
|
||||
*/
|
||||
static int do_mem_probe(struct pcmcia_socket *s, u_long base, u_long num,
|
||||
|
|
|
|||
|
|
@ -3037,13 +3037,6 @@ static int ab8500_fg_bind(struct device *dev, struct device *master,
|
|||
{
|
||||
struct ab8500_fg *di = dev_get_drvdata(dev);
|
||||
|
||||
/* Create a work queue for running the FG algorithm */
|
||||
di->fg_wq = alloc_ordered_workqueue("ab8500_fg_wq", WQ_MEM_RECLAIM);
|
||||
if (di->fg_wq == NULL) {
|
||||
dev_err(dev, "failed to create work queue\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
di->bat_cap.max_mah_design = di->bm->bi->charge_full_design_uah;
|
||||
di->bat_cap.max_mah = di->bat_cap.max_mah_design;
|
||||
di->vbat_nom_uv = di->bm->bi->voltage_max_design_uv;
|
||||
|
|
@ -3067,8 +3060,7 @@ static void ab8500_fg_unbind(struct device *dev, struct device *master,
|
|||
if (ret)
|
||||
dev_err(dev, "failed to disable coulomb counter\n");
|
||||
|
||||
destroy_workqueue(di->fg_wq);
|
||||
flush_scheduled_work();
|
||||
flush_workqueue(di->fg_wq);
|
||||
}
|
||||
|
||||
static const struct component_ops ab8500_fg_component_ops = {
|
||||
|
|
@ -3117,6 +3109,13 @@ static int ab8500_fg_probe(struct platform_device *pdev)
|
|||
ab8500_fg_charge_state_to(di, AB8500_FG_CHARGE_INIT);
|
||||
ab8500_fg_discharge_state_to(di, AB8500_FG_DISCHARGE_INIT);
|
||||
|
||||
/* Create a work queue for running the FG algorithm */
|
||||
di->fg_wq = alloc_ordered_workqueue("ab8500_fg_wq", WQ_MEM_RECLAIM);
|
||||
if (di->fg_wq == NULL) {
|
||||
dev_err(dev, "failed to create work queue\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Init work for running the fg algorithm instantly */
|
||||
INIT_WORK(&di->fg_work, ab8500_fg_instant_work);
|
||||
|
||||
|
|
@ -3227,6 +3226,8 @@ static int ab8500_fg_remove(struct platform_device *pdev)
|
|||
{
|
||||
struct ab8500_fg *di = platform_get_drvdata(pdev);
|
||||
|
||||
destroy_workqueue(di->fg_wq);
|
||||
flush_scheduled_work();
|
||||
component_del(&pdev->dev, &ab8500_fg_component_ops);
|
||||
list_del(&di->node);
|
||||
ab8500_fg_sysfs_exit(di);
|
||||
|
|
|
|||
|
|
@ -90,6 +90,8 @@
|
|||
#define AXP288_REG_UPDATE_INTERVAL (60 * HZ)
|
||||
#define AXP288_FG_INTR_NUM 6
|
||||
|
||||
#define AXP288_QUIRK_NO_BATTERY BIT(0)
|
||||
|
||||
static bool no_current_sense_res;
|
||||
module_param(no_current_sense_res, bool, 0444);
|
||||
MODULE_PARM_DESC(no_current_sense_res, "No (or broken) current sense resistor");
|
||||
|
|
@ -524,7 +526,7 @@ static struct power_supply_desc fuel_gauge_desc = {
|
|||
* detection reports one despite it not being there.
|
||||
* Please keep this listed sorted alphabetically.
|
||||
*/
|
||||
static const struct dmi_system_id axp288_no_battery_list[] = {
|
||||
static const struct dmi_system_id axp288_quirks[] = {
|
||||
{
|
||||
/* ACEPC T8 Cherry Trail Z8350 mini PC */
|
||||
.matches = {
|
||||
|
|
@ -534,6 +536,7 @@ static const struct dmi_system_id axp288_no_battery_list[] = {
|
|||
/* also match on somewhat unique bios-version */
|
||||
DMI_EXACT_MATCH(DMI_BIOS_VERSION, "1.000"),
|
||||
},
|
||||
.driver_data = (void *)AXP288_QUIRK_NO_BATTERY,
|
||||
},
|
||||
{
|
||||
/* ACEPC T11 Cherry Trail Z8350 mini PC */
|
||||
|
|
@ -544,6 +547,7 @@ static const struct dmi_system_id axp288_no_battery_list[] = {
|
|||
/* also match on somewhat unique bios-version */
|
||||
DMI_EXACT_MATCH(DMI_BIOS_VERSION, "1.000"),
|
||||
},
|
||||
.driver_data = (void *)AXP288_QUIRK_NO_BATTERY,
|
||||
},
|
||||
{
|
||||
/* Intel Cherry Trail Compute Stick, Windows version */
|
||||
|
|
@ -551,6 +555,7 @@ static const struct dmi_system_id axp288_no_battery_list[] = {
|
|||
DMI_MATCH(DMI_SYS_VENDOR, "Intel"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "STK1AW32SC"),
|
||||
},
|
||||
.driver_data = (void *)AXP288_QUIRK_NO_BATTERY,
|
||||
},
|
||||
{
|
||||
/* Intel Cherry Trail Compute Stick, version without an OS */
|
||||
|
|
@ -558,34 +563,54 @@ static const struct dmi_system_id axp288_no_battery_list[] = {
|
|||
DMI_MATCH(DMI_SYS_VENDOR, "Intel"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "STK1A32SC"),
|
||||
},
|
||||
.driver_data = (void *)AXP288_QUIRK_NO_BATTERY,
|
||||
},
|
||||
{
|
||||
/* Meegopad T02 */
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "MEEGOPAD T02"),
|
||||
},
|
||||
.driver_data = (void *)AXP288_QUIRK_NO_BATTERY,
|
||||
},
|
||||
{ /* Mele PCG03 Mini PC */
|
||||
.matches = {
|
||||
DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Mini PC"),
|
||||
DMI_EXACT_MATCH(DMI_BOARD_NAME, "Mini PC"),
|
||||
},
|
||||
.driver_data = (void *)AXP288_QUIRK_NO_BATTERY,
|
||||
},
|
||||
{
|
||||
/* Minix Neo Z83-4 mini PC */
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "MINIX"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Z83-4"),
|
||||
}
|
||||
},
|
||||
.driver_data = (void *)AXP288_QUIRK_NO_BATTERY,
|
||||
},
|
||||
{
|
||||
/* Various Ace PC/Meegopad/MinisForum/Wintel Mini-PCs/HDMI-sticks */
|
||||
/*
|
||||
* One Mix 1, this uses the "T3 MRD" boardname used by
|
||||
* generic mini PCs, but it is a mini laptop so it does
|
||||
* actually have a battery!
|
||||
*/
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_NAME, "T3 MRD"),
|
||||
DMI_MATCH(DMI_BIOS_DATE, "06/14/2018"),
|
||||
},
|
||||
.driver_data = NULL,
|
||||
},
|
||||
{
|
||||
/*
|
||||
* Various Ace PC/Meegopad/MinisForum/Wintel Mini-PCs/HDMI-sticks
|
||||
* This entry must be last because it is generic, this allows
|
||||
* adding more specifuc quirks overriding this generic entry.
|
||||
*/
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_NAME, "T3 MRD"),
|
||||
DMI_MATCH(DMI_CHASSIS_TYPE, "3"),
|
||||
DMI_MATCH(DMI_BIOS_VENDOR, "American Megatrends Inc."),
|
||||
DMI_MATCH(DMI_BIOS_VERSION, "5.11"),
|
||||
},
|
||||
.driver_data = (void *)AXP288_QUIRK_NO_BATTERY,
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
|
@ -665,7 +690,9 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev)
|
|||
[BAT_D_CURR] = "axp288-chrg-d-curr",
|
||||
[BAT_VOLT] = "axp288-batt-volt",
|
||||
};
|
||||
const struct dmi_system_id *dmi_id;
|
||||
struct device *dev = &pdev->dev;
|
||||
unsigned long quirks = 0;
|
||||
int i, pirq, ret;
|
||||
|
||||
/*
|
||||
|
|
@ -675,7 +702,11 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev)
|
|||
if (!acpi_quirk_skip_acpi_ac_and_battery())
|
||||
return -ENODEV;
|
||||
|
||||
if (dmi_check_system(axp288_no_battery_list))
|
||||
dmi_id = dmi_first_match(axp288_quirks);
|
||||
if (dmi_id)
|
||||
quirks = (unsigned long)dmi_id->driver_data;
|
||||
|
||||
if (quirks & AXP288_QUIRK_NO_BATTERY)
|
||||
return -ENODEV;
|
||||
|
||||
info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
|
||||
|
|
|
|||
|
|
@ -455,11 +455,9 @@ static ssize_t bq24190_sysfs_show(struct device *dev,
|
|||
if (!info)
|
||||
return -EINVAL;
|
||||
|
||||
ret = pm_runtime_get_sync(bdi->dev);
|
||||
if (ret < 0) {
|
||||
pm_runtime_put_noidle(bdi->dev);
|
||||
ret = pm_runtime_resume_and_get(bdi->dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = bq24190_read_mask(bdi, info->reg, info->mask, info->shift, &v);
|
||||
if (ret)
|
||||
|
|
@ -490,11 +488,9 @@ static ssize_t bq24190_sysfs_store(struct device *dev,
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = pm_runtime_get_sync(bdi->dev);
|
||||
if (ret < 0) {
|
||||
pm_runtime_put_noidle(bdi->dev);
|
||||
ret = pm_runtime_resume_and_get(bdi->dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = bq24190_write_mask(bdi, info->reg, info->mask, info->shift, v);
|
||||
if (ret)
|
||||
|
|
@ -512,10 +508,9 @@ static int bq24190_set_otg_vbus(struct bq24190_dev_info *bdi, bool enable)
|
|||
union power_supply_propval val = { .intval = bdi->charge_type };
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_get_sync(bdi->dev);
|
||||
ret = pm_runtime_resume_and_get(bdi->dev);
|
||||
if (ret < 0) {
|
||||
dev_warn(bdi->dev, "pm_runtime_get failed: %i\n", ret);
|
||||
pm_runtime_put_noidle(bdi->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -551,10 +546,9 @@ static int bq24190_vbus_is_enabled(struct regulator_dev *dev)
|
|||
int ret;
|
||||
u8 val;
|
||||
|
||||
ret = pm_runtime_get_sync(bdi->dev);
|
||||
ret = pm_runtime_resume_and_get(bdi->dev);
|
||||
if (ret < 0) {
|
||||
dev_warn(bdi->dev, "pm_runtime_get failed: %i\n", ret);
|
||||
pm_runtime_put_noidle(bdi->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -1128,11 +1122,9 @@ static int bq24190_charger_get_property(struct power_supply *psy,
|
|||
|
||||
dev_dbg(bdi->dev, "prop: %d\n", psp);
|
||||
|
||||
ret = pm_runtime_get_sync(bdi->dev);
|
||||
if (ret < 0) {
|
||||
pm_runtime_put_noidle(bdi->dev);
|
||||
ret = pm_runtime_resume_and_get(bdi->dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_CHARGE_TYPE:
|
||||
|
|
@ -1204,11 +1196,9 @@ static int bq24190_charger_set_property(struct power_supply *psy,
|
|||
|
||||
dev_dbg(bdi->dev, "prop: %d\n", psp);
|
||||
|
||||
ret = pm_runtime_get_sync(bdi->dev);
|
||||
if (ret < 0) {
|
||||
pm_runtime_put_noidle(bdi->dev);
|
||||
ret = pm_runtime_resume_and_get(bdi->dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_ONLINE:
|
||||
|
|
@ -1477,11 +1467,9 @@ static int bq24190_battery_get_property(struct power_supply *psy,
|
|||
dev_warn(bdi->dev, "warning: /sys/class/power_supply/bq24190-battery is deprecated\n");
|
||||
dev_dbg(bdi->dev, "prop: %d\n", psp);
|
||||
|
||||
ret = pm_runtime_get_sync(bdi->dev);
|
||||
if (ret < 0) {
|
||||
pm_runtime_put_noidle(bdi->dev);
|
||||
ret = pm_runtime_resume_and_get(bdi->dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_STATUS:
|
||||
|
|
@ -1525,11 +1513,9 @@ static int bq24190_battery_set_property(struct power_supply *psy,
|
|||
dev_warn(bdi->dev, "warning: /sys/class/power_supply/bq24190-battery is deprecated\n");
|
||||
dev_dbg(bdi->dev, "prop: %d\n", psp);
|
||||
|
||||
ret = pm_runtime_get_sync(bdi->dev);
|
||||
if (ret < 0) {
|
||||
pm_runtime_put_noidle(bdi->dev);
|
||||
ret = pm_runtime_resume_and_get(bdi->dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_ONLINE:
|
||||
|
|
@ -1683,10 +1669,9 @@ static irqreturn_t bq24190_irq_handler_thread(int irq, void *data)
|
|||
int error;
|
||||
|
||||
bdi->irq_event = true;
|
||||
error = pm_runtime_get_sync(bdi->dev);
|
||||
error = pm_runtime_resume_and_get(bdi->dev);
|
||||
if (error < 0) {
|
||||
dev_warn(bdi->dev, "pm_runtime_get failed: %i\n", error);
|
||||
pm_runtime_put_noidle(bdi->dev);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
bq24190_check_status(bdi);
|
||||
|
|
@ -1921,11 +1906,9 @@ static int bq24190_remove(struct i2c_client *client)
|
|||
struct bq24190_dev_info *bdi = i2c_get_clientdata(client);
|
||||
int error;
|
||||
|
||||
error = pm_runtime_get_sync(bdi->dev);
|
||||
if (error < 0) {
|
||||
error = pm_runtime_resume_and_get(bdi->dev);
|
||||
if (error < 0)
|
||||
dev_warn(bdi->dev, "pm_runtime_get failed: %i\n", error);
|
||||
pm_runtime_put_noidle(bdi->dev);
|
||||
}
|
||||
|
||||
bq24190_register_reset(bdi);
|
||||
if (bdi->battery)
|
||||
|
|
@ -1982,11 +1965,9 @@ static __maybe_unused int bq24190_pm_suspend(struct device *dev)
|
|||
struct bq24190_dev_info *bdi = i2c_get_clientdata(client);
|
||||
int error;
|
||||
|
||||
error = pm_runtime_get_sync(bdi->dev);
|
||||
if (error < 0) {
|
||||
error = pm_runtime_resume_and_get(bdi->dev);
|
||||
if (error < 0)
|
||||
dev_warn(bdi->dev, "pm_runtime_get failed: %i\n", error);
|
||||
pm_runtime_put_noidle(bdi->dev);
|
||||
}
|
||||
|
||||
bq24190_register_reset(bdi);
|
||||
|
||||
|
|
@ -2007,11 +1988,9 @@ static __maybe_unused int bq24190_pm_resume(struct device *dev)
|
|||
bdi->f_reg = 0;
|
||||
bdi->ss_reg = BQ24190_REG_SS_VBUS_STAT_MASK; /* impossible state */
|
||||
|
||||
error = pm_runtime_get_sync(bdi->dev);
|
||||
if (error < 0) {
|
||||
error = pm_runtime_resume_and_get(bdi->dev);
|
||||
if (error < 0)
|
||||
dev_warn(bdi->dev, "pm_runtime_get failed: %i\n", error);
|
||||
pm_runtime_put_noidle(bdi->dev);
|
||||
}
|
||||
|
||||
bq24190_register_reset(bdi);
|
||||
bq24190_set_config(bdi);
|
||||
|
|
|
|||
|
|
@ -1572,14 +1572,6 @@ static int bq27xxx_battery_read_charge(struct bq27xxx_device_info *di, u8 reg)
|
|||
*/
|
||||
static inline int bq27xxx_battery_read_nac(struct bq27xxx_device_info *di)
|
||||
{
|
||||
int flags;
|
||||
|
||||
if (di->opts & BQ27XXX_O_ZERO) {
|
||||
flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, true);
|
||||
if (flags >= 0 && (flags & BQ27000_FLAG_CI))
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
return bq27xxx_battery_read_charge(di, BQ27XXX_REG_NAC);
|
||||
}
|
||||
|
||||
|
|
@ -1742,6 +1734,18 @@ static bool bq27xxx_battery_dead(struct bq27xxx_device_info *di, u16 flags)
|
|||
return flags & (BQ27XXX_FLAG_SOC1 | BQ27XXX_FLAG_SOCF);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if reported battery capacity is inaccurate
|
||||
*/
|
||||
static bool bq27xxx_battery_capacity_inaccurate(struct bq27xxx_device_info *di,
|
||||
u16 flags)
|
||||
{
|
||||
if (di->opts & BQ27XXX_O_HAS_CI)
|
||||
return (flags & BQ27000_FLAG_CI);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
static int bq27xxx_battery_read_health(struct bq27xxx_device_info *di)
|
||||
{
|
||||
/* Unlikely but important to return first */
|
||||
|
|
@ -1751,6 +1755,8 @@ static int bq27xxx_battery_read_health(struct bq27xxx_device_info *di)
|
|||
return POWER_SUPPLY_HEALTH_COLD;
|
||||
if (unlikely(bq27xxx_battery_dead(di, di->cache.flags)))
|
||||
return POWER_SUPPLY_HEALTH_DEAD;
|
||||
if (unlikely(bq27xxx_battery_capacity_inaccurate(di, di->cache.flags)))
|
||||
return POWER_SUPPLY_HEALTH_CALIBRATION_REQUIRED;
|
||||
|
||||
return POWER_SUPPLY_HEALTH_GOOD;
|
||||
}
|
||||
|
|
@ -1758,7 +1764,6 @@ static int bq27xxx_battery_read_health(struct bq27xxx_device_info *di)
|
|||
void bq27xxx_battery_update(struct bq27xxx_device_info *di)
|
||||
{
|
||||
struct bq27xxx_reg_cache cache = {0, };
|
||||
bool has_ci_flag = di->opts & BQ27XXX_O_HAS_CI;
|
||||
bool has_singe_flag = di->opts & BQ27XXX_O_ZERO;
|
||||
|
||||
cache.flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, has_singe_flag);
|
||||
|
|
@ -1766,30 +1771,19 @@ void bq27xxx_battery_update(struct bq27xxx_device_info *di)
|
|||
cache.flags = -1; /* read error */
|
||||
if (cache.flags >= 0) {
|
||||
cache.temperature = bq27xxx_battery_read_temperature(di);
|
||||
if (has_ci_flag && (cache.flags & BQ27000_FLAG_CI)) {
|
||||
dev_info_once(di->dev, "battery is not calibrated! ignoring capacity values\n");
|
||||
cache.capacity = -ENODATA;
|
||||
cache.energy = -ENODATA;
|
||||
cache.time_to_empty = -ENODATA;
|
||||
cache.time_to_empty_avg = -ENODATA;
|
||||
cache.time_to_full = -ENODATA;
|
||||
cache.charge_full = -ENODATA;
|
||||
cache.health = -ENODATA;
|
||||
} else {
|
||||
if (di->regs[BQ27XXX_REG_TTE] != INVALID_REG_ADDR)
|
||||
cache.time_to_empty = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTE);
|
||||
if (di->regs[BQ27XXX_REG_TTECP] != INVALID_REG_ADDR)
|
||||
cache.time_to_empty_avg = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTECP);
|
||||
if (di->regs[BQ27XXX_REG_TTF] != INVALID_REG_ADDR)
|
||||
cache.time_to_full = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTF);
|
||||
if (di->regs[BQ27XXX_REG_TTE] != INVALID_REG_ADDR)
|
||||
cache.time_to_empty = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTE);
|
||||
if (di->regs[BQ27XXX_REG_TTECP] != INVALID_REG_ADDR)
|
||||
cache.time_to_empty_avg = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTECP);
|
||||
if (di->regs[BQ27XXX_REG_TTF] != INVALID_REG_ADDR)
|
||||
cache.time_to_full = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTF);
|
||||
|
||||
cache.charge_full = bq27xxx_battery_read_fcc(di);
|
||||
cache.capacity = bq27xxx_battery_read_soc(di);
|
||||
if (di->regs[BQ27XXX_REG_AE] != INVALID_REG_ADDR)
|
||||
cache.energy = bq27xxx_battery_read_energy(di);
|
||||
di->cache.flags = cache.flags;
|
||||
cache.health = bq27xxx_battery_read_health(di);
|
||||
}
|
||||
cache.charge_full = bq27xxx_battery_read_fcc(di);
|
||||
cache.capacity = bq27xxx_battery_read_soc(di);
|
||||
if (di->regs[BQ27XXX_REG_AE] != INVALID_REG_ADDR)
|
||||
cache.energy = bq27xxx_battery_read_energy(di);
|
||||
di->cache.flags = cache.flags;
|
||||
cache.health = bq27xxx_battery_read_health(di);
|
||||
if (di->regs[BQ27XXX_REG_CYCT] != INVALID_REG_ADDR)
|
||||
cache.cycle_count = bq27xxx_battery_read_cyct(di);
|
||||
|
||||
|
|
|
|||
|
|
@ -694,7 +694,7 @@ int power_supply_get_battery_info(struct power_supply *psy,
|
|||
goto out_put_node;
|
||||
}
|
||||
|
||||
info = devm_kmalloc(&psy->dev, sizeof(*info), GFP_KERNEL);
|
||||
info = devm_kzalloc(&psy->dev, sizeof(*info), GFP_KERNEL);
|
||||
if (!info) {
|
||||
err = -ENOMEM;
|
||||
goto out_put_node;
|
||||
|
|
|
|||
|
|
@ -572,6 +572,17 @@ config PWM_SUN4I
|
|||
To compile this driver as a module, choose M here: the module
|
||||
will be called pwm-sun4i.
|
||||
|
||||
config PWM_SUNPLUS
|
||||
tristate "Sunplus PWM support"
|
||||
depends on ARCH_SUNPLUS || COMPILE_TEST
|
||||
depends on HAS_IOMEM && OF
|
||||
help
|
||||
Generic PWM framework driver for the PWM controller on
|
||||
Sunplus SoCs.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called pwm-sunplus.
|
||||
|
||||
config PWM_TEGRA
|
||||
tristate "NVIDIA Tegra PWM support"
|
||||
depends on ARCH_TEGRA || COMPILE_TEST
|
||||
|
|
@ -640,4 +651,18 @@ config PWM_VT8500
|
|||
To compile this driver as a module, choose M here: the module
|
||||
will be called pwm-vt8500.
|
||||
|
||||
config PWM_XILINX
|
||||
tristate "Xilinx AXI Timer PWM support"
|
||||
depends on OF_ADDRESS
|
||||
depends on COMMON_CLK
|
||||
select REGMAP_MMIO
|
||||
help
|
||||
PWM driver for Xilinx LogiCORE IP AXI timers. This timer is
|
||||
typically a soft core which may be present in Xilinx FPGAs.
|
||||
This device may also be present in Microblaze soft processors.
|
||||
If you don't have this IP in your design, choose N.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called pwm-xilinx.
|
||||
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ obj-$(CONFIG_PWM_STM32) += pwm-stm32.o
|
|||
obj-$(CONFIG_PWM_STM32_LP) += pwm-stm32-lp.o
|
||||
obj-$(CONFIG_PWM_STMPE) += pwm-stmpe.o
|
||||
obj-$(CONFIG_PWM_SUN4I) += pwm-sun4i.o
|
||||
obj-$(CONFIG_PWM_SUNPLUS) += pwm-sunplus.o
|
||||
obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o
|
||||
obj-$(CONFIG_PWM_TIECAP) += pwm-tiecap.o
|
||||
obj-$(CONFIG_PWM_TIEHRPWM) += pwm-tiehrpwm.o
|
||||
|
|
@ -60,3 +61,4 @@ obj-$(CONFIG_PWM_TWL) += pwm-twl.o
|
|||
obj-$(CONFIG_PWM_TWL_LED) += pwm-twl-led.o
|
||||
obj-$(CONFIG_PWM_VISCONTI) += pwm-visconti.o
|
||||
obj-$(CONFIG_PWM_VT8500) += pwm-vt8500.o
|
||||
obj-$(CONFIG_PWM_XILINX) += pwm-xilinx.o
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ struct atmel_tcb_pwm_chip {
|
|||
struct atmel_tcb_channel bkup;
|
||||
};
|
||||
|
||||
const u8 atmel_tcb_divisors[] = { 2, 8, 32, 128, 0, };
|
||||
static const u8 atmel_tcb_divisors[] = { 2, 8, 32, 128, 0, };
|
||||
|
||||
static inline struct atmel_tcb_pwm_chip *to_tcb_chip(struct pwm_chip *chip)
|
||||
{
|
||||
|
|
@ -72,7 +72,8 @@ static int atmel_tcb_pwm_set_polarity(struct pwm_chip *chip,
|
|||
struct pwm_device *pwm,
|
||||
enum pwm_polarity polarity)
|
||||
{
|
||||
struct atmel_tcb_pwm_device *tcbpwm = pwm_get_chip_data(pwm);
|
||||
struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip);
|
||||
struct atmel_tcb_pwm_device *tcbpwm = tcbpwmc->pwms[pwm->hwpwm];
|
||||
|
||||
tcbpwm->polarity = polarity;
|
||||
|
||||
|
|
@ -97,7 +98,6 @@ static int atmel_tcb_pwm_request(struct pwm_chip *chip,
|
|||
return ret;
|
||||
}
|
||||
|
||||
pwm_set_chip_data(pwm, tcbpwm);
|
||||
tcbpwm->polarity = PWM_POLARITY_NORMAL;
|
||||
tcbpwm->duty = 0;
|
||||
tcbpwm->period = 0;
|
||||
|
|
@ -139,7 +139,7 @@ static int atmel_tcb_pwm_request(struct pwm_chip *chip,
|
|||
static void atmel_tcb_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
|
||||
{
|
||||
struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip);
|
||||
struct atmel_tcb_pwm_device *tcbpwm = pwm_get_chip_data(pwm);
|
||||
struct atmel_tcb_pwm_device *tcbpwm = tcbpwmc->pwms[pwm->hwpwm];
|
||||
|
||||
clk_disable_unprepare(tcbpwmc->clk);
|
||||
tcbpwmc->pwms[pwm->hwpwm] = NULL;
|
||||
|
|
@ -149,7 +149,7 @@ static void atmel_tcb_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
|
|||
static void atmel_tcb_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
|
||||
{
|
||||
struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip);
|
||||
struct atmel_tcb_pwm_device *tcbpwm = pwm_get_chip_data(pwm);
|
||||
struct atmel_tcb_pwm_device *tcbpwm = tcbpwmc->pwms[pwm->hwpwm];
|
||||
unsigned cmr;
|
||||
enum pwm_polarity polarity = tcbpwm->polarity;
|
||||
|
||||
|
|
@ -206,7 +206,7 @@ static void atmel_tcb_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
|
|||
static int atmel_tcb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
|
||||
{
|
||||
struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip);
|
||||
struct atmel_tcb_pwm_device *tcbpwm = pwm_get_chip_data(pwm);
|
||||
struct atmel_tcb_pwm_device *tcbpwm = tcbpwmc->pwms[pwm->hwpwm];
|
||||
u32 cmr;
|
||||
enum pwm_polarity polarity = tcbpwm->polarity;
|
||||
|
||||
|
|
@ -291,7 +291,7 @@ static int atmel_tcb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
|
|||
int duty_ns, int period_ns)
|
||||
{
|
||||
struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip);
|
||||
struct atmel_tcb_pwm_device *tcbpwm = pwm_get_chip_data(pwm);
|
||||
struct atmel_tcb_pwm_device *tcbpwm = tcbpwmc->pwms[pwm->hwpwm];
|
||||
struct atmel_tcb_pwm_device *atcbpwm = NULL;
|
||||
int i = 0;
|
||||
int slowclk = 0;
|
||||
|
|
|
|||
|
|
@ -23,29 +23,6 @@ static inline struct clps711x_chip *to_clps711x_chip(struct pwm_chip *chip)
|
|||
return container_of(chip, struct clps711x_chip, chip);
|
||||
}
|
||||
|
||||
static void clps711x_pwm_update_val(struct clps711x_chip *priv, u32 n, u32 v)
|
||||
{
|
||||
/* PWM0 - bits 4..7, PWM1 - bits 8..11 */
|
||||
u32 shift = (n + 1) * 4;
|
||||
unsigned long flags;
|
||||
u32 tmp;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
|
||||
tmp = readl(priv->pmpcon);
|
||||
tmp &= ~(0xf << shift);
|
||||
tmp |= v << shift;
|
||||
writel(tmp, priv->pmpcon);
|
||||
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
}
|
||||
|
||||
static unsigned int clps711x_get_duty(struct pwm_device *pwm, unsigned int v)
|
||||
{
|
||||
/* Duty cycle 0..15 max */
|
||||
return DIV64_U64_ROUND_CLOSEST(v * 0xf, pwm->args.period);
|
||||
}
|
||||
|
||||
static int clps711x_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
|
||||
{
|
||||
struct clps711x_chip *priv = to_clps711x_chip(chip);
|
||||
|
|
@ -60,44 +37,41 @@ static int clps711x_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int clps711x_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
int duty_ns, int period_ns)
|
||||
static int clps711x_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
const struct pwm_state *state)
|
||||
{
|
||||
struct clps711x_chip *priv = to_clps711x_chip(chip);
|
||||
unsigned int duty;
|
||||
/* PWM0 - bits 4..7, PWM1 - bits 8..11 */
|
||||
u32 shift = (pwm->hwpwm + 1) * 4;
|
||||
unsigned long flags;
|
||||
u32 pmpcon, val;
|
||||
|
||||
if (period_ns != pwm->args.period)
|
||||
if (state->polarity != PWM_POLARITY_NORMAL)
|
||||
return -EINVAL;
|
||||
|
||||
duty = clps711x_get_duty(pwm, duty_ns);
|
||||
clps711x_pwm_update_val(priv, pwm->hwpwm, duty);
|
||||
if (state->period != pwm->args.period)
|
||||
return -EINVAL;
|
||||
|
||||
if (state->enabled)
|
||||
val = mul_u64_u64_div_u64(state->duty_cycle, 0xf, state->period);
|
||||
else
|
||||
val = 0;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
|
||||
pmpcon = readl(priv->pmpcon);
|
||||
pmpcon &= ~(0xf << shift);
|
||||
pmpcon |= val << shift;
|
||||
writel(pmpcon, priv->pmpcon);
|
||||
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int clps711x_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
|
||||
{
|
||||
struct clps711x_chip *priv = to_clps711x_chip(chip);
|
||||
unsigned int duty;
|
||||
|
||||
duty = clps711x_get_duty(pwm, pwm_get_duty_cycle(pwm));
|
||||
clps711x_pwm_update_val(priv, pwm->hwpwm, duty);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void clps711x_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
|
||||
{
|
||||
struct clps711x_chip *priv = to_clps711x_chip(chip);
|
||||
|
||||
clps711x_pwm_update_val(priv, pwm->hwpwm, 0);
|
||||
}
|
||||
|
||||
static const struct pwm_ops clps711x_pwm_ops = {
|
||||
.request = clps711x_pwm_request,
|
||||
.config = clps711x_pwm_config,
|
||||
.enable = clps711x_pwm_enable,
|
||||
.disable = clps711x_pwm_disable,
|
||||
.apply = clps711x_pwm_apply,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -12,17 +12,21 @@
|
|||
#include <linux/pwm.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <dt-bindings/mfd/cros_ec.h>
|
||||
|
||||
/**
|
||||
* struct cros_ec_pwm_device - Driver data for EC PWM
|
||||
*
|
||||
* @dev: Device node
|
||||
* @ec: Pointer to EC device
|
||||
* @chip: PWM controller chip
|
||||
* @use_pwm_type: Use PWM types instead of generic channels
|
||||
*/
|
||||
struct cros_ec_pwm_device {
|
||||
struct device *dev;
|
||||
struct cros_ec_device *ec;
|
||||
struct pwm_chip chip;
|
||||
bool use_pwm_type;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -58,14 +62,31 @@ static void cros_ec_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
|
|||
kfree(channel);
|
||||
}
|
||||
|
||||
static int cros_ec_pwm_set_duty(struct cros_ec_device *ec, u8 index, u16 duty)
|
||||
static int cros_ec_dt_type_to_pwm_type(u8 dt_index, u8 *pwm_type)
|
||||
{
|
||||
switch (dt_index) {
|
||||
case CROS_EC_PWM_DT_KB_LIGHT:
|
||||
*pwm_type = EC_PWM_TYPE_KB_LIGHT;
|
||||
return 0;
|
||||
case CROS_EC_PWM_DT_DISPLAY_LIGHT:
|
||||
*pwm_type = EC_PWM_TYPE_DISPLAY_LIGHT;
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int cros_ec_pwm_set_duty(struct cros_ec_pwm_device *ec_pwm, u8 index,
|
||||
u16 duty)
|
||||
{
|
||||
struct cros_ec_device *ec = ec_pwm->ec;
|
||||
struct {
|
||||
struct cros_ec_command msg;
|
||||
struct ec_params_pwm_set_duty params;
|
||||
} __packed buf;
|
||||
struct ec_params_pwm_set_duty *params = &buf.params;
|
||||
struct cros_ec_command *msg = &buf.msg;
|
||||
int ret;
|
||||
|
||||
memset(&buf, 0, sizeof(buf));
|
||||
|
||||
|
|
@ -75,14 +96,25 @@ static int cros_ec_pwm_set_duty(struct cros_ec_device *ec, u8 index, u16 duty)
|
|||
msg->outsize = sizeof(*params);
|
||||
|
||||
params->duty = duty;
|
||||
params->pwm_type = EC_PWM_TYPE_GENERIC;
|
||||
params->index = index;
|
||||
|
||||
if (ec_pwm->use_pwm_type) {
|
||||
ret = cros_ec_dt_type_to_pwm_type(index, ¶ms->pwm_type);
|
||||
if (ret) {
|
||||
dev_err(ec->dev, "Invalid PWM type index: %d\n", index);
|
||||
return ret;
|
||||
}
|
||||
params->index = 0;
|
||||
} else {
|
||||
params->pwm_type = EC_PWM_TYPE_GENERIC;
|
||||
params->index = index;
|
||||
}
|
||||
|
||||
return cros_ec_cmd_xfer_status(ec, msg);
|
||||
}
|
||||
|
||||
static int cros_ec_pwm_get_duty(struct cros_ec_device *ec, u8 index)
|
||||
static int cros_ec_pwm_get_duty(struct cros_ec_pwm_device *ec_pwm, u8 index)
|
||||
{
|
||||
struct cros_ec_device *ec = ec_pwm->ec;
|
||||
struct {
|
||||
struct cros_ec_command msg;
|
||||
union {
|
||||
|
|
@ -102,8 +134,17 @@ static int cros_ec_pwm_get_duty(struct cros_ec_device *ec, u8 index)
|
|||
msg->insize = sizeof(*resp);
|
||||
msg->outsize = sizeof(*params);
|
||||
|
||||
params->pwm_type = EC_PWM_TYPE_GENERIC;
|
||||
params->index = index;
|
||||
if (ec_pwm->use_pwm_type) {
|
||||
ret = cros_ec_dt_type_to_pwm_type(index, ¶ms->pwm_type);
|
||||
if (ret) {
|
||||
dev_err(ec->dev, "Invalid PWM type index: %d\n", index);
|
||||
return ret;
|
||||
}
|
||||
params->index = 0;
|
||||
} else {
|
||||
params->pwm_type = EC_PWM_TYPE_GENERIC;
|
||||
params->index = index;
|
||||
}
|
||||
|
||||
ret = cros_ec_cmd_xfer_status(ec, msg);
|
||||
if (ret < 0)
|
||||
|
|
@ -133,7 +174,7 @@ static int cros_ec_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
|||
*/
|
||||
duty_cycle = state->enabled ? state->duty_cycle : 0;
|
||||
|
||||
ret = cros_ec_pwm_set_duty(ec_pwm->ec, pwm->hwpwm, duty_cycle);
|
||||
ret = cros_ec_pwm_set_duty(ec_pwm, pwm->hwpwm, duty_cycle);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
|
@ -149,7 +190,7 @@ static void cros_ec_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
|
|||
struct cros_ec_pwm *channel = pwm_get_chip_data(pwm);
|
||||
int ret;
|
||||
|
||||
ret = cros_ec_pwm_get_duty(ec_pwm->ec, pwm->hwpwm);
|
||||
ret = cros_ec_pwm_get_duty(ec_pwm, pwm->hwpwm);
|
||||
if (ret < 0) {
|
||||
dev_err(chip->dev, "error getting initial duty: %d\n", ret);
|
||||
return;
|
||||
|
|
@ -204,13 +245,13 @@ static const struct pwm_ops cros_ec_pwm_ops = {
|
|||
* of PWMs it supports directly, so we have to read the pwm duty cycle for
|
||||
* subsequent channels until we get an error.
|
||||
*/
|
||||
static int cros_ec_num_pwms(struct cros_ec_device *ec)
|
||||
static int cros_ec_num_pwms(struct cros_ec_pwm_device *ec_pwm)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
/* The index field is only 8 bits */
|
||||
for (i = 0; i <= U8_MAX; i++) {
|
||||
ret = cros_ec_pwm_get_duty(ec, i);
|
||||
ret = cros_ec_pwm_get_duty(ec_pwm, i);
|
||||
/*
|
||||
* We look for SUCCESS, INVALID_COMMAND, or INVALID_PARAM
|
||||
* responses; everything else is treated as an error.
|
||||
|
|
@ -236,6 +277,7 @@ static int cros_ec_pwm_probe(struct platform_device *pdev)
|
|||
{
|
||||
struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct cros_ec_pwm_device *ec_pwm;
|
||||
struct pwm_chip *chip;
|
||||
int ret;
|
||||
|
|
@ -251,17 +293,26 @@ static int cros_ec_pwm_probe(struct platform_device *pdev)
|
|||
chip = &ec_pwm->chip;
|
||||
ec_pwm->ec = ec;
|
||||
|
||||
if (of_device_is_compatible(np, "google,cros-ec-pwm-type"))
|
||||
ec_pwm->use_pwm_type = true;
|
||||
|
||||
/* PWM chip */
|
||||
chip->dev = dev;
|
||||
chip->ops = &cros_ec_pwm_ops;
|
||||
chip->of_xlate = cros_ec_pwm_xlate;
|
||||
chip->of_pwm_n_cells = 1;
|
||||
ret = cros_ec_num_pwms(ec);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Couldn't find PWMs: %d\n", ret);
|
||||
return ret;
|
||||
|
||||
if (ec_pwm->use_pwm_type) {
|
||||
chip->npwm = CROS_EC_PWM_DT_COUNT;
|
||||
} else {
|
||||
ret = cros_ec_num_pwms(ec_pwm);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Couldn't find PWMs: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
chip->npwm = ret;
|
||||
}
|
||||
chip->npwm = ret;
|
||||
|
||||
dev_dbg(dev, "Probed %u PWMs\n", chip->npwm);
|
||||
|
||||
ret = pwmchip_add(chip);
|
||||
|
|
@ -288,6 +339,7 @@ static int cros_ec_pwm_remove(struct platform_device *dev)
|
|||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id cros_ec_pwm_of_match[] = {
|
||||
{ .compatible = "google,cros-ec-pwm" },
|
||||
{ .compatible = "google,cros-ec-pwm-type" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, cros_ec_pwm_of_match);
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ static void lp3943_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
|
|||
}
|
||||
|
||||
static int lp3943_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
int duty_ns, int period_ns)
|
||||
u64 duty_ns, u64 period_ns)
|
||||
{
|
||||
struct lp3943_pwm *lp3943_pwm = to_lp3943_pwm(chip);
|
||||
struct lp3943 *lp3943 = lp3943_pwm->lp3943;
|
||||
|
|
@ -118,14 +118,20 @@ static int lp3943_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
|
|||
reg_duty = LP3943_REG_PWM1;
|
||||
}
|
||||
|
||||
period_ns = clamp(period_ns, LP3943_MIN_PERIOD, LP3943_MAX_PERIOD);
|
||||
val = (u8)(period_ns / LP3943_MIN_PERIOD - 1);
|
||||
/*
|
||||
* Note that after this clamping, period_ns fits into an int. This is
|
||||
* helpful because we can resort to integer division below instead of
|
||||
* the (more expensive) 64 bit division.
|
||||
*/
|
||||
period_ns = clamp(period_ns, (u64)LP3943_MIN_PERIOD, (u64)LP3943_MAX_PERIOD);
|
||||
val = (u8)((int)period_ns / LP3943_MIN_PERIOD - 1);
|
||||
|
||||
err = lp3943_write_byte(lp3943, reg_prescale, val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
val = (u8)(duty_ns * LP3943_MAX_DUTY / period_ns);
|
||||
duty_ns = min(duty_ns, period_ns);
|
||||
val = (u8)((int)duty_ns * LP3943_MAX_DUTY / (int)period_ns);
|
||||
|
||||
return lp3943_write_byte(lp3943, reg_duty, val);
|
||||
}
|
||||
|
|
@ -182,12 +188,34 @@ static void lp3943_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
|
|||
lp3943_pwm_set_mode(lp3943_pwm, pwm_map, LP3943_GPIO_OUT_HIGH);
|
||||
}
|
||||
|
||||
static int lp3943_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
const struct pwm_state *state)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (state->polarity != PWM_POLARITY_NORMAL)
|
||||
return -EINVAL;
|
||||
|
||||
if (!state->enabled) {
|
||||
if (pwm->state.enabled)
|
||||
lp3943_pwm_disable(chip, pwm);
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = lp3943_pwm_config(chip, pwm, state->duty_cycle, state->period);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!pwm->state.enabled)
|
||||
err = lp3943_pwm_enable(chip, pwm);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct pwm_ops lp3943_pwm_ops = {
|
||||
.request = lp3943_pwm_request,
|
||||
.free = lp3943_pwm_free,
|
||||
.config = lp3943_pwm_config,
|
||||
.enable = lp3943_pwm_enable,
|
||||
.disable = lp3943_pwm_disable,
|
||||
.apply = lp3943_pwm_apply,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -226,14 +226,7 @@ static int lpc18xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int lpc18xx_pwm_set_polarity(struct pwm_chip *chip,
|
||||
struct pwm_device *pwm,
|
||||
enum pwm_polarity polarity)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lpc18xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
|
||||
static int lpc18xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm, enum pwm_polarity polarity)
|
||||
{
|
||||
struct lpc18xx_pwm_chip *lpc18xx_pwm = to_lpc18xx_pwm_chip(chip);
|
||||
struct lpc18xx_pwm_data *lpc18xx_data = &lpc18xx_pwm->channeldata[pwm->hwpwm];
|
||||
|
|
@ -249,7 +242,7 @@ static int lpc18xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
|
|||
LPC18XX_PWM_EVSTATEMSK(lpc18xx_data->duty_event),
|
||||
LPC18XX_PWM_EVSTATEMSK_ALL);
|
||||
|
||||
if (pwm_get_polarity(pwm) == PWM_POLARITY_NORMAL) {
|
||||
if (polarity == PWM_POLARITY_NORMAL) {
|
||||
set_event = lpc18xx_pwm->period_event;
|
||||
clear_event = lpc18xx_data->duty_event;
|
||||
res_action = LPC18XX_PWM_RES_SET;
|
||||
|
|
@ -308,11 +301,35 @@ static void lpc18xx_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
|
|||
clear_bit(lpc18xx_data->duty_event, &lpc18xx_pwm->event_map);
|
||||
}
|
||||
|
||||
static int lpc18xx_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
const struct pwm_state *state)
|
||||
{
|
||||
int err;
|
||||
bool enabled = pwm->state.enabled;
|
||||
|
||||
if (state->polarity != pwm->state.polarity && pwm->state.enabled) {
|
||||
lpc18xx_pwm_disable(chip, pwm);
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
if (!state->enabled) {
|
||||
if (enabled)
|
||||
lpc18xx_pwm_disable(chip, pwm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = lpc18xx_pwm_config(pwm->chip, pwm, state->duty_cycle, state->period);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!enabled)
|
||||
err = lpc18xx_pwm_enable(chip, pwm, state->polarity);
|
||||
|
||||
return err;
|
||||
}
|
||||
static const struct pwm_ops lpc18xx_pwm_ops = {
|
||||
.config = lpc18xx_pwm_config,
|
||||
.set_polarity = lpc18xx_pwm_set_polarity,
|
||||
.enable = lpc18xx_pwm_enable,
|
||||
.disable = lpc18xx_pwm_disable,
|
||||
.apply = lpc18xx_pwm_apply,
|
||||
.request = lpc18xx_pwm_request,
|
||||
.free = lpc18xx_pwm_free,
|
||||
.owner = THIS_MODULE,
|
||||
|
|
|
|||
|
|
@ -88,10 +88,33 @@ static void lpc32xx_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
|
|||
clk_disable_unprepare(lpc32xx->clk);
|
||||
}
|
||||
|
||||
static int lpc32xx_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
const struct pwm_state *state)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (state->polarity != PWM_POLARITY_NORMAL)
|
||||
return -EINVAL;
|
||||
|
||||
if (!state->enabled) {
|
||||
if (pwm->state.enabled)
|
||||
lpc32xx_pwm_disable(chip, pwm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = lpc32xx_pwm_config(pwm->chip, pwm, state->duty_cycle, state->period);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!pwm->state.enabled)
|
||||
err = lpc32xx_pwm_enable(chip, pwm);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct pwm_ops lpc32xx_pwm_ops = {
|
||||
.config = lpc32xx_pwm_config,
|
||||
.enable = lpc32xx_pwm_enable,
|
||||
.disable = lpc32xx_pwm_disable,
|
||||
.apply = lpc32xx_pwm_apply,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -198,10 +198,33 @@ static void pwm_mediatek_disable(struct pwm_chip *chip, struct pwm_device *pwm)
|
|||
pwm_mediatek_clk_disable(chip, pwm);
|
||||
}
|
||||
|
||||
static int pwm_mediatek_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
const struct pwm_state *state)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (state->polarity != PWM_POLARITY_NORMAL)
|
||||
return -EINVAL;
|
||||
|
||||
if (!state->enabled) {
|
||||
if (pwm->state.enabled)
|
||||
pwm_mediatek_disable(chip, pwm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = pwm_mediatek_config(pwm->chip, pwm, state->duty_cycle, state->period);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!pwm->state.enabled)
|
||||
err = pwm_mediatek_enable(chip, pwm);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct pwm_ops pwm_mediatek_ops = {
|
||||
.config = pwm_mediatek_config,
|
||||
.enable = pwm_mediatek_enable,
|
||||
.disable = pwm_mediatek_disable,
|
||||
.apply = pwm_mediatek_apply,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
|
|
@ -264,6 +287,12 @@ static const struct pwm_mediatek_of_data mt2712_pwm_data = {
|
|||
.has_ck_26m_sel = false,
|
||||
};
|
||||
|
||||
static const struct pwm_mediatek_of_data mt6795_pwm_data = {
|
||||
.num_pwms = 7,
|
||||
.pwm45_fixup = false,
|
||||
.has_ck_26m_sel = false,
|
||||
};
|
||||
|
||||
static const struct pwm_mediatek_of_data mt7622_pwm_data = {
|
||||
.num_pwms = 6,
|
||||
.pwm45_fixup = false,
|
||||
|
|
@ -302,6 +331,7 @@ static const struct pwm_mediatek_of_data mt8516_pwm_data = {
|
|||
|
||||
static const struct of_device_id pwm_mediatek_of_match[] = {
|
||||
{ .compatible = "mediatek,mt2712-pwm", .data = &mt2712_pwm_data },
|
||||
{ .compatible = "mediatek,mt6795-pwm", .data = &mt6795_pwm_data },
|
||||
{ .compatible = "mediatek,mt7622-pwm", .data = &mt7622_pwm_data },
|
||||
{ .compatible = "mediatek,mt7623-pwm", .data = &mt7623_pwm_data },
|
||||
{ .compatible = "mediatek,mt7628-pwm", .data = &mt7628_pwm_data },
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ static int raspberrypi_pwm_get_property(struct rpi_firmware *firmware,
|
|||
u32 reg, u32 *val)
|
||||
{
|
||||
struct raspberrypi_pwm_prop msg = {
|
||||
.reg = reg
|
||||
.reg = cpu_to_le32(reg),
|
||||
};
|
||||
int ret;
|
||||
|
||||
|
|
|
|||
|
|
@ -89,71 +89,71 @@ struct tpu_device {
|
|||
|
||||
#define to_tpu_device(c) container_of(c, struct tpu_device, chip)
|
||||
|
||||
static void tpu_pwm_write(struct tpu_pwm_device *pwm, int reg_nr, u16 value)
|
||||
static void tpu_pwm_write(struct tpu_pwm_device *tpd, int reg_nr, u16 value)
|
||||
{
|
||||
void __iomem *base = pwm->tpu->base + TPU_CHANNEL_OFFSET
|
||||
+ pwm->channel * TPU_CHANNEL_SIZE;
|
||||
void __iomem *base = tpd->tpu->base + TPU_CHANNEL_OFFSET
|
||||
+ tpd->channel * TPU_CHANNEL_SIZE;
|
||||
|
||||
iowrite16(value, base + reg_nr);
|
||||
}
|
||||
|
||||
static void tpu_pwm_set_pin(struct tpu_pwm_device *pwm,
|
||||
static void tpu_pwm_set_pin(struct tpu_pwm_device *tpd,
|
||||
enum tpu_pin_state state)
|
||||
{
|
||||
static const char * const states[] = { "inactive", "PWM", "active" };
|
||||
|
||||
dev_dbg(&pwm->tpu->pdev->dev, "%u: configuring pin as %s\n",
|
||||
pwm->channel, states[state]);
|
||||
dev_dbg(&tpd->tpu->pdev->dev, "%u: configuring pin as %s\n",
|
||||
tpd->channel, states[state]);
|
||||
|
||||
switch (state) {
|
||||
case TPU_PIN_INACTIVE:
|
||||
tpu_pwm_write(pwm, TPU_TIORn,
|
||||
pwm->polarity == PWM_POLARITY_INVERSED ?
|
||||
tpu_pwm_write(tpd, TPU_TIORn,
|
||||
tpd->polarity == PWM_POLARITY_INVERSED ?
|
||||
TPU_TIOR_IOA_1 : TPU_TIOR_IOA_0);
|
||||
break;
|
||||
case TPU_PIN_PWM:
|
||||
tpu_pwm_write(pwm, TPU_TIORn,
|
||||
pwm->polarity == PWM_POLARITY_INVERSED ?
|
||||
tpu_pwm_write(tpd, TPU_TIORn,
|
||||
tpd->polarity == PWM_POLARITY_INVERSED ?
|
||||
TPU_TIOR_IOA_0_SET : TPU_TIOR_IOA_1_CLR);
|
||||
break;
|
||||
case TPU_PIN_ACTIVE:
|
||||
tpu_pwm_write(pwm, TPU_TIORn,
|
||||
pwm->polarity == PWM_POLARITY_INVERSED ?
|
||||
tpu_pwm_write(tpd, TPU_TIORn,
|
||||
tpd->polarity == PWM_POLARITY_INVERSED ?
|
||||
TPU_TIOR_IOA_0 : TPU_TIOR_IOA_1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void tpu_pwm_start_stop(struct tpu_pwm_device *pwm, int start)
|
||||
static void tpu_pwm_start_stop(struct tpu_pwm_device *tpd, int start)
|
||||
{
|
||||
unsigned long flags;
|
||||
u16 value;
|
||||
|
||||
spin_lock_irqsave(&pwm->tpu->lock, flags);
|
||||
value = ioread16(pwm->tpu->base + TPU_TSTR);
|
||||
spin_lock_irqsave(&tpd->tpu->lock, flags);
|
||||
value = ioread16(tpd->tpu->base + TPU_TSTR);
|
||||
|
||||
if (start)
|
||||
value |= 1 << pwm->channel;
|
||||
value |= 1 << tpd->channel;
|
||||
else
|
||||
value &= ~(1 << pwm->channel);
|
||||
value &= ~(1 << tpd->channel);
|
||||
|
||||
iowrite16(value, pwm->tpu->base + TPU_TSTR);
|
||||
spin_unlock_irqrestore(&pwm->tpu->lock, flags);
|
||||
iowrite16(value, tpd->tpu->base + TPU_TSTR);
|
||||
spin_unlock_irqrestore(&tpd->tpu->lock, flags);
|
||||
}
|
||||
|
||||
static int tpu_pwm_timer_start(struct tpu_pwm_device *pwm)
|
||||
static int tpu_pwm_timer_start(struct tpu_pwm_device *tpd)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!pwm->timer_on) {
|
||||
if (!tpd->timer_on) {
|
||||
/* Wake up device and enable clock. */
|
||||
pm_runtime_get_sync(&pwm->tpu->pdev->dev);
|
||||
ret = clk_prepare_enable(pwm->tpu->clk);
|
||||
pm_runtime_get_sync(&tpd->tpu->pdev->dev);
|
||||
ret = clk_prepare_enable(tpd->tpu->clk);
|
||||
if (ret) {
|
||||
dev_err(&pwm->tpu->pdev->dev, "cannot enable clock\n");
|
||||
dev_err(&tpd->tpu->pdev->dev, "cannot enable clock\n");
|
||||
return ret;
|
||||
}
|
||||
pwm->timer_on = true;
|
||||
tpd->timer_on = true;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -161,8 +161,8 @@ static int tpu_pwm_timer_start(struct tpu_pwm_device *pwm)
|
|||
* completely. First drive the pin to the inactive state to avoid
|
||||
* glitches.
|
||||
*/
|
||||
tpu_pwm_set_pin(pwm, TPU_PIN_INACTIVE);
|
||||
tpu_pwm_start_stop(pwm, false);
|
||||
tpu_pwm_set_pin(tpd, TPU_PIN_INACTIVE);
|
||||
tpu_pwm_start_stop(tpd, false);
|
||||
|
||||
/*
|
||||
* - Clear TCNT on TGRB match
|
||||
|
|
@ -172,142 +172,168 @@ static int tpu_pwm_timer_start(struct tpu_pwm_device *pwm)
|
|||
* - Output 1 until TGRA, output 0 until TGRB (active high polarity
|
||||
* - PWM mode
|
||||
*/
|
||||
tpu_pwm_write(pwm, TPU_TCRn, TPU_TCR_CCLR_TGRB | TPU_TCR_CKEG_RISING |
|
||||
pwm->prescaler);
|
||||
tpu_pwm_write(pwm, TPU_TMDRn, TPU_TMDR_MD_PWM);
|
||||
tpu_pwm_set_pin(pwm, TPU_PIN_PWM);
|
||||
tpu_pwm_write(pwm, TPU_TGRAn, pwm->duty);
|
||||
tpu_pwm_write(pwm, TPU_TGRBn, pwm->period);
|
||||
tpu_pwm_write(tpd, TPU_TCRn, TPU_TCR_CCLR_TGRB | TPU_TCR_CKEG_RISING |
|
||||
tpd->prescaler);
|
||||
tpu_pwm_write(tpd, TPU_TMDRn, TPU_TMDR_MD_PWM);
|
||||
tpu_pwm_set_pin(tpd, TPU_PIN_PWM);
|
||||
tpu_pwm_write(tpd, TPU_TGRAn, tpd->duty);
|
||||
tpu_pwm_write(tpd, TPU_TGRBn, tpd->period);
|
||||
|
||||
dev_dbg(&pwm->tpu->pdev->dev, "%u: TGRA 0x%04x TGRB 0x%04x\n",
|
||||
pwm->channel, pwm->duty, pwm->period);
|
||||
dev_dbg(&tpd->tpu->pdev->dev, "%u: TGRA 0x%04x TGRB 0x%04x\n",
|
||||
tpd->channel, tpd->duty, tpd->period);
|
||||
|
||||
/* Start the channel. */
|
||||
tpu_pwm_start_stop(pwm, true);
|
||||
tpu_pwm_start_stop(tpd, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tpu_pwm_timer_stop(struct tpu_pwm_device *pwm)
|
||||
static void tpu_pwm_timer_stop(struct tpu_pwm_device *tpd)
|
||||
{
|
||||
if (!pwm->timer_on)
|
||||
if (!tpd->timer_on)
|
||||
return;
|
||||
|
||||
/* Disable channel. */
|
||||
tpu_pwm_start_stop(pwm, false);
|
||||
tpu_pwm_start_stop(tpd, false);
|
||||
|
||||
/* Stop clock and mark device as idle. */
|
||||
clk_disable_unprepare(pwm->tpu->clk);
|
||||
pm_runtime_put(&pwm->tpu->pdev->dev);
|
||||
clk_disable_unprepare(tpd->tpu->clk);
|
||||
pm_runtime_put(&tpd->tpu->pdev->dev);
|
||||
|
||||
pwm->timer_on = false;
|
||||
tpd->timer_on = false;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* PWM API
|
||||
*/
|
||||
|
||||
static int tpu_pwm_request(struct pwm_chip *chip, struct pwm_device *_pwm)
|
||||
static int tpu_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
|
||||
{
|
||||
struct tpu_device *tpu = to_tpu_device(chip);
|
||||
struct tpu_pwm_device *pwm;
|
||||
struct tpu_pwm_device *tpd;
|
||||
|
||||
if (_pwm->hwpwm >= TPU_CHANNEL_MAX)
|
||||
if (pwm->hwpwm >= TPU_CHANNEL_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
pwm = kzalloc(sizeof(*pwm), GFP_KERNEL);
|
||||
if (pwm == NULL)
|
||||
tpd = kzalloc(sizeof(*tpd), GFP_KERNEL);
|
||||
if (tpd == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
pwm->tpu = tpu;
|
||||
pwm->channel = _pwm->hwpwm;
|
||||
pwm->polarity = PWM_POLARITY_NORMAL;
|
||||
pwm->prescaler = 0;
|
||||
pwm->period = 0;
|
||||
pwm->duty = 0;
|
||||
tpd->tpu = tpu;
|
||||
tpd->channel = pwm->hwpwm;
|
||||
tpd->polarity = PWM_POLARITY_NORMAL;
|
||||
tpd->prescaler = 0;
|
||||
tpd->period = 0;
|
||||
tpd->duty = 0;
|
||||
|
||||
pwm->timer_on = false;
|
||||
tpd->timer_on = false;
|
||||
|
||||
pwm_set_chip_data(_pwm, pwm);
|
||||
pwm_set_chip_data(pwm, tpd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tpu_pwm_free(struct pwm_chip *chip, struct pwm_device *_pwm)
|
||||
static void tpu_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
|
||||
{
|
||||
struct tpu_pwm_device *pwm = pwm_get_chip_data(_pwm);
|
||||
struct tpu_pwm_device *tpd = pwm_get_chip_data(pwm);
|
||||
|
||||
tpu_pwm_timer_stop(pwm);
|
||||
kfree(pwm);
|
||||
tpu_pwm_timer_stop(tpd);
|
||||
kfree(tpd);
|
||||
}
|
||||
|
||||
static int tpu_pwm_config(struct pwm_chip *chip, struct pwm_device *_pwm,
|
||||
int duty_ns, int period_ns)
|
||||
static int tpu_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
u64 duty_ns, u64 period_ns, bool enabled)
|
||||
{
|
||||
static const unsigned int prescalers[] = { 1, 4, 16, 64 };
|
||||
struct tpu_pwm_device *pwm = pwm_get_chip_data(_pwm);
|
||||
struct tpu_pwm_device *tpd = pwm_get_chip_data(pwm);
|
||||
struct tpu_device *tpu = to_tpu_device(chip);
|
||||
unsigned int prescaler;
|
||||
bool duty_only = false;
|
||||
u32 clk_rate;
|
||||
u32 period;
|
||||
u64 period;
|
||||
u32 duty;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Pick a prescaler to avoid overflowing the counter.
|
||||
* TODO: Pick the highest acceptable prescaler.
|
||||
*/
|
||||
clk_rate = clk_get_rate(tpu->clk);
|
||||
|
||||
for (prescaler = 0; prescaler < ARRAY_SIZE(prescalers); ++prescaler) {
|
||||
period = clk_rate / prescalers[prescaler]
|
||||
/ (NSEC_PER_SEC / period_ns);
|
||||
if (period <= 0xffff)
|
||||
break;
|
||||
if (unlikely(clk_rate > NSEC_PER_SEC)) {
|
||||
/*
|
||||
* This won't happen in the nearer future, so this is only a
|
||||
* safeguard to prevent the following calculation from
|
||||
* overflowing. With this clk_rate * period_ns / NSEC_PER_SEC is
|
||||
* not greater than period_ns and so fits into an u64.
|
||||
*/
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (prescaler == ARRAY_SIZE(prescalers) || period == 0) {
|
||||
dev_err(&tpu->pdev->dev, "clock rate mismatch\n");
|
||||
return -ENOTSUPP;
|
||||
period = mul_u64_u64_div_u64(clk_rate, period_ns, NSEC_PER_SEC);
|
||||
|
||||
/*
|
||||
* Find the minimal prescaler in [0..3] such that
|
||||
*
|
||||
* period >> (2 * prescaler) < 0x10000
|
||||
*
|
||||
* This could be calculated using something like:
|
||||
*
|
||||
* prescaler = max(ilog2(period) / 2, 7) - 7;
|
||||
*
|
||||
* but given there are only four allowed results and that ilog2 isn't
|
||||
* cheap on all platforms using a switch statement is more effective.
|
||||
*/
|
||||
switch (period) {
|
||||
case 1 ... 0xffff:
|
||||
prescaler = 0;
|
||||
break;
|
||||
|
||||
case 0x10000 ... 0x3ffff:
|
||||
prescaler = 1;
|
||||
break;
|
||||
|
||||
case 0x40000 ... 0xfffff:
|
||||
prescaler = 2;
|
||||
break;
|
||||
|
||||
case 0x100000 ... 0x3fffff:
|
||||
prescaler = 3;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (duty_ns) {
|
||||
duty = clk_rate / prescalers[prescaler]
|
||||
/ (NSEC_PER_SEC / duty_ns);
|
||||
if (duty > period)
|
||||
return -EINVAL;
|
||||
} else {
|
||||
period >>= 2 * prescaler;
|
||||
|
||||
if (duty_ns)
|
||||
duty = mul_u64_u64_div_u64(clk_rate, duty_ns,
|
||||
(u64)NSEC_PER_SEC << (2 * prescaler));
|
||||
else
|
||||
duty = 0;
|
||||
}
|
||||
|
||||
dev_dbg(&tpu->pdev->dev,
|
||||
"rate %u, prescaler %u, period %u, duty %u\n",
|
||||
clk_rate, prescalers[prescaler], period, duty);
|
||||
clk_rate, 1 << (2 * prescaler), (u32)period, duty);
|
||||
|
||||
if (pwm->prescaler == prescaler && pwm->period == period)
|
||||
if (tpd->prescaler == prescaler && tpd->period == period)
|
||||
duty_only = true;
|
||||
|
||||
pwm->prescaler = prescaler;
|
||||
pwm->period = period;
|
||||
pwm->duty = duty;
|
||||
tpd->prescaler = prescaler;
|
||||
tpd->period = period;
|
||||
tpd->duty = duty;
|
||||
|
||||
/* If the channel is disabled we're done. */
|
||||
if (!pwm_is_enabled(_pwm))
|
||||
if (!enabled)
|
||||
return 0;
|
||||
|
||||
if (duty_only && pwm->timer_on) {
|
||||
if (duty_only && tpd->timer_on) {
|
||||
/*
|
||||
* If only the duty cycle changed and the timer is already
|
||||
* running, there's no need to reconfigure it completely, Just
|
||||
* modify the duty cycle.
|
||||
*/
|
||||
tpu_pwm_write(pwm, TPU_TGRAn, pwm->duty);
|
||||
dev_dbg(&tpu->pdev->dev, "%u: TGRA 0x%04x\n", pwm->channel,
|
||||
pwm->duty);
|
||||
tpu_pwm_write(tpd, TPU_TGRAn, tpd->duty);
|
||||
dev_dbg(&tpu->pdev->dev, "%u: TGRA 0x%04x\n", tpd->channel,
|
||||
tpd->duty);
|
||||
} else {
|
||||
/* Otherwise perform a full reconfiguration. */
|
||||
ret = tpu_pwm_timer_start(pwm);
|
||||
ret = tpu_pwm_timer_start(tpd);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -317,29 +343,29 @@ static int tpu_pwm_config(struct pwm_chip *chip, struct pwm_device *_pwm,
|
|||
* To avoid running the timer when not strictly required, handle
|
||||
* 0% and 100% duty cycles as fixed levels and stop the timer.
|
||||
*/
|
||||
tpu_pwm_set_pin(pwm, duty ? TPU_PIN_ACTIVE : TPU_PIN_INACTIVE);
|
||||
tpu_pwm_timer_stop(pwm);
|
||||
tpu_pwm_set_pin(tpd, duty ? TPU_PIN_ACTIVE : TPU_PIN_INACTIVE);
|
||||
tpu_pwm_timer_stop(tpd);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpu_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *_pwm,
|
||||
static int tpu_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
enum pwm_polarity polarity)
|
||||
{
|
||||
struct tpu_pwm_device *pwm = pwm_get_chip_data(_pwm);
|
||||
struct tpu_pwm_device *tpd = pwm_get_chip_data(pwm);
|
||||
|
||||
pwm->polarity = polarity;
|
||||
tpd->polarity = polarity;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpu_pwm_enable(struct pwm_chip *chip, struct pwm_device *_pwm)
|
||||
static int tpu_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
|
||||
{
|
||||
struct tpu_pwm_device *pwm = pwm_get_chip_data(_pwm);
|
||||
struct tpu_pwm_device *tpd = pwm_get_chip_data(pwm);
|
||||
int ret;
|
||||
|
||||
ret = tpu_pwm_timer_start(pwm);
|
||||
ret = tpu_pwm_timer_start(tpd);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
|
@ -347,32 +373,64 @@ static int tpu_pwm_enable(struct pwm_chip *chip, struct pwm_device *_pwm)
|
|||
* To avoid running the timer when not strictly required, handle 0% and
|
||||
* 100% duty cycles as fixed levels and stop the timer.
|
||||
*/
|
||||
if (pwm->duty == 0 || pwm->duty == pwm->period) {
|
||||
tpu_pwm_set_pin(pwm, pwm->duty ?
|
||||
if (tpd->duty == 0 || tpd->duty == tpd->period) {
|
||||
tpu_pwm_set_pin(tpd, tpd->duty ?
|
||||
TPU_PIN_ACTIVE : TPU_PIN_INACTIVE);
|
||||
tpu_pwm_timer_stop(pwm);
|
||||
tpu_pwm_timer_stop(tpd);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tpu_pwm_disable(struct pwm_chip *chip, struct pwm_device *_pwm)
|
||||
static void tpu_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
|
||||
{
|
||||
struct tpu_pwm_device *pwm = pwm_get_chip_data(_pwm);
|
||||
struct tpu_pwm_device *tpd = pwm_get_chip_data(pwm);
|
||||
|
||||
/* The timer must be running to modify the pin output configuration. */
|
||||
tpu_pwm_timer_start(pwm);
|
||||
tpu_pwm_set_pin(pwm, TPU_PIN_INACTIVE);
|
||||
tpu_pwm_timer_stop(pwm);
|
||||
tpu_pwm_timer_start(tpd);
|
||||
tpu_pwm_set_pin(tpd, TPU_PIN_INACTIVE);
|
||||
tpu_pwm_timer_stop(tpd);
|
||||
}
|
||||
|
||||
static int tpu_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
const struct pwm_state *state)
|
||||
{
|
||||
int err;
|
||||
bool enabled = pwm->state.enabled;
|
||||
|
||||
if (state->polarity != pwm->state.polarity) {
|
||||
if (enabled) {
|
||||
tpu_pwm_disable(chip, pwm);
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
err = tpu_pwm_set_polarity(chip, pwm, state->polarity);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!state->enabled) {
|
||||
if (enabled)
|
||||
tpu_pwm_disable(chip, pwm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = tpu_pwm_config(pwm->chip, pwm,
|
||||
state->duty_cycle, state->period, enabled);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!enabled)
|
||||
err = tpu_pwm_enable(chip, pwm);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct pwm_ops tpu_pwm_ops = {
|
||||
.request = tpu_pwm_request,
|
||||
.free = tpu_pwm_free,
|
||||
.config = tpu_pwm_config,
|
||||
.set_polarity = tpu_pwm_set_polarity,
|
||||
.enable = tpu_pwm_enable,
|
||||
.disable = tpu_pwm_disable,
|
||||
.apply = tpu_pwm_apply,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
|
|
@ -398,10 +456,8 @@ static int tpu_probe(struct platform_device *pdev)
|
|||
return PTR_ERR(tpu->base);
|
||||
|
||||
tpu->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(tpu->clk)) {
|
||||
dev_err(&pdev->dev, "cannot get clock\n");
|
||||
return PTR_ERR(tpu->clk);
|
||||
}
|
||||
if (IS_ERR(tpu->clk))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(tpu->clk), "Failed to get clock\n");
|
||||
|
||||
/* Initialize and register the device. */
|
||||
platform_set_drvdata(pdev, tpu);
|
||||
|
|
@ -410,25 +466,13 @@ static int tpu_probe(struct platform_device *pdev)
|
|||
tpu->chip.ops = &tpu_pwm_ops;
|
||||
tpu->chip.npwm = TPU_CHANNEL_MAX;
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
ret = devm_pm_runtime_enable(&pdev->dev);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(&pdev->dev, ret, "Failed to enable runtime PM\n");
|
||||
|
||||
ret = pwmchip_add(&tpu->chip);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to register PWM chip\n");
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpu_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct tpu_device *tpu = platform_get_drvdata(pdev);
|
||||
|
||||
pwmchip_remove(&tpu->chip);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
ret = devm_pwmchip_add(&pdev->dev, &tpu->chip);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(&pdev->dev, ret, "Failed to register PWM chip\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -447,7 +491,6 @@ MODULE_DEVICE_TABLE(of, tpu_of_table);
|
|||
|
||||
static struct platform_driver tpu_driver = {
|
||||
.probe = tpu_probe,
|
||||
.remove = tpu_remove,
|
||||
.driver = {
|
||||
.name = "renesas-tpu-pwm",
|
||||
.of_match_table = of_match_ptr(tpu_of_table),
|
||||
|
|
|
|||
|
|
@ -321,14 +321,6 @@ static int __pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm,
|
|||
struct samsung_pwm_channel *chan = pwm_get_chip_data(pwm);
|
||||
u32 tin_ns = chan->tin_ns, tcnt, tcmp, oldtcmp;
|
||||
|
||||
/*
|
||||
* We currently avoid using 64bit arithmetic by using the
|
||||
* fact that anything faster than 1Hz is easily representable
|
||||
* by 32bits.
|
||||
*/
|
||||
if (period_ns > NSEC_PER_SEC)
|
||||
return -ERANGE;
|
||||
|
||||
tcnt = readl(our_chip->base + REG_TCNTB(pwm->hwpwm));
|
||||
oldtcmp = readl(our_chip->base + REG_TCMPB(pwm->hwpwm));
|
||||
|
||||
|
|
@ -438,13 +430,51 @@ static int pwm_samsung_set_polarity(struct pwm_chip *chip,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int pwm_samsung_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
const struct pwm_state *state)
|
||||
{
|
||||
int err, enabled = pwm->state.enabled;
|
||||
|
||||
if (state->polarity != pwm->state.polarity) {
|
||||
if (enabled) {
|
||||
pwm_samsung_disable(chip, pwm);
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
err = pwm_samsung_set_polarity(chip, pwm, state->polarity);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!state->enabled) {
|
||||
if (enabled)
|
||||
pwm_samsung_disable(chip, pwm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* We currently avoid using 64bit arithmetic by using the
|
||||
* fact that anything faster than 1Hz is easily representable
|
||||
* by 32bits.
|
||||
*/
|
||||
if (state->period > NSEC_PER_SEC)
|
||||
return -ERANGE;
|
||||
|
||||
err = pwm_samsung_config(chip, pwm, state->duty_cycle, state->period);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!pwm->state.enabled)
|
||||
err = pwm_samsung_enable(chip, pwm);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct pwm_ops pwm_samsung_ops = {
|
||||
.request = pwm_samsung_request,
|
||||
.free = pwm_samsung_free,
|
||||
.enable = pwm_samsung_enable,
|
||||
.disable = pwm_samsung_disable,
|
||||
.config = pwm_samsung_config,
|
||||
.set_polarity = pwm_samsung_set_polarity,
|
||||
.apply = pwm_samsung_apply,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -138,10 +138,9 @@ static int pwm_sifive_enable(struct pwm_chip *chip, bool enable)
|
|||
dev_err(ddata->chip.dev, "Enable clk failed\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (!enable)
|
||||
} else {
|
||||
clk_disable(ddata->clk);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -391,11 +391,34 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int sti_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
const struct pwm_state *state)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (state->polarity != PWM_POLARITY_NORMAL)
|
||||
return -EINVAL;
|
||||
|
||||
if (!state->enabled) {
|
||||
if (pwm->state.enabled)
|
||||
sti_pwm_disable(chip, pwm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = sti_pwm_config(pwm->chip, pwm, state->duty_cycle, state->period);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!pwm->state.enabled)
|
||||
err = sti_pwm_enable(chip, pwm);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct pwm_ops sti_pwm_ops = {
|
||||
.capture = sti_pwm_capture,
|
||||
.config = sti_pwm_config,
|
||||
.enable = sti_pwm_enable,
|
||||
.disable = sti_pwm_disable,
|
||||
.apply = sti_pwm_apply,
|
||||
.free = sti_pwm_free,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -259,10 +259,33 @@ static int stmpe_24xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int stmpe_24xx_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
const struct pwm_state *state)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (state->polarity != PWM_POLARITY_NORMAL)
|
||||
return -EINVAL;
|
||||
|
||||
if (!state->enabled) {
|
||||
if (pwm->state.enabled)
|
||||
stmpe_24xx_pwm_disable(chip, pwm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = stmpe_24xx_pwm_config(pwm->chip, pwm, state->duty_cycle, state->period);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!pwm->state.enabled)
|
||||
err = stmpe_24xx_pwm_enable(chip, pwm);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct pwm_ops stmpe_24xx_pwm_ops = {
|
||||
.config = stmpe_24xx_pwm_config,
|
||||
.enable = stmpe_24xx_pwm_enable,
|
||||
.disable = stmpe_24xx_pwm_disable,
|
||||
.apply = stmpe_24xx_pwm_apply,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -89,7 +89,6 @@ struct sun4i_pwm_chip {
|
|||
void __iomem *base;
|
||||
spinlock_t ctrl_lock;
|
||||
const struct sun4i_pwm_data *data;
|
||||
unsigned long next_period[2];
|
||||
};
|
||||
|
||||
static inline struct sun4i_pwm_chip *to_sun4i_pwm_chip(struct pwm_chip *chip)
|
||||
|
|
@ -236,7 +235,6 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
|||
u32 ctrl, duty = 0, period = 0, val;
|
||||
int ret;
|
||||
unsigned int delay_us, prescaler = 0;
|
||||
unsigned long now;
|
||||
bool bypass;
|
||||
|
||||
pwm_get_state(pwm, &cstate);
|
||||
|
|
@ -284,8 +282,6 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
|||
|
||||
val = (duty & PWM_DTY_MASK) | PWM_PRD(period);
|
||||
sun4i_pwm_writel(sun4i_pwm, val, PWM_CH_PRD(pwm->hwpwm));
|
||||
sun4i_pwm->next_period[pwm->hwpwm] = jiffies +
|
||||
nsecs_to_jiffies(cstate.period + 1000);
|
||||
|
||||
if (state->polarity != PWM_POLARITY_NORMAL)
|
||||
ctrl &= ~BIT_CH(PWM_ACT_STATE, pwm->hwpwm);
|
||||
|
|
@ -305,15 +301,11 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
|||
return 0;
|
||||
|
||||
/* We need a full period to elapse before disabling the channel. */
|
||||
now = jiffies;
|
||||
if (time_before(now, sun4i_pwm->next_period[pwm->hwpwm])) {
|
||||
delay_us = jiffies_to_usecs(sun4i_pwm->next_period[pwm->hwpwm] -
|
||||
now);
|
||||
if ((delay_us / 500) > MAX_UDELAY_MS)
|
||||
msleep(delay_us / 1000 + 1);
|
||||
else
|
||||
usleep_range(delay_us, delay_us * 2);
|
||||
}
|
||||
delay_us = DIV_ROUND_UP_ULL(cstate.period, NSEC_PER_USEC);
|
||||
if ((delay_us / 500) > MAX_UDELAY_MS)
|
||||
msleep(delay_us / 1000 + 1);
|
||||
else
|
||||
usleep_range(delay_us, delay_us * 2);
|
||||
|
||||
spin_lock(&sun4i_pwm->ctrl_lock);
|
||||
ctrl = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG);
|
||||
|
|
|
|||
232
drivers/pwm/pwm-sunplus.c
Normal file
232
drivers/pwm/pwm-sunplus.c
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* PWM device driver for SUNPLUS SP7021 SoC
|
||||
*
|
||||
* Links:
|
||||
* Reference Manual:
|
||||
* https://sunplus-tibbo.atlassian.net/wiki/spaces/doc/overview
|
||||
*
|
||||
* Reference Manual(PWM module):
|
||||
* https://sunplus.atlassian.net/wiki/spaces/doc/pages/461144198/12.+Pulse+Width+Modulation+PWM
|
||||
*
|
||||
* Limitations:
|
||||
* - Only supports normal polarity.
|
||||
* - It output low when PWM channel disabled.
|
||||
* - When the parameters change, current running period will not be completed
|
||||
* and run new settings immediately.
|
||||
* - In .apply() PWM output need to write register FREQ and DUTY. When first write FREQ
|
||||
* done and not yet write DUTY, it has short timing gap use new FREQ and old DUTY.
|
||||
*
|
||||
* Author: Hammer Hsieh <hammerh0314@gmail.com>
|
||||
*/
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pwm.h>
|
||||
|
||||
#define SP7021_PWM_MODE0 0x000
|
||||
#define SP7021_PWM_MODE0_PWMEN(ch) BIT(ch)
|
||||
#define SP7021_PWM_MODE0_BYPASS(ch) BIT(8 + (ch))
|
||||
#define SP7021_PWM_MODE1 0x004
|
||||
#define SP7021_PWM_MODE1_CNT_EN(ch) BIT(ch)
|
||||
#define SP7021_PWM_FREQ(ch) (0x008 + 4 * (ch))
|
||||
#define SP7021_PWM_FREQ_MAX GENMASK(15, 0)
|
||||
#define SP7021_PWM_DUTY(ch) (0x018 + 4 * (ch))
|
||||
#define SP7021_PWM_DUTY_DD_SEL(ch) FIELD_PREP(GENMASK(9, 8), ch)
|
||||
#define SP7021_PWM_DUTY_MAX GENMASK(7, 0)
|
||||
#define SP7021_PWM_DUTY_MASK SP7021_PWM_DUTY_MAX
|
||||
#define SP7021_PWM_FREQ_SCALER 256
|
||||
#define SP7021_PWM_NUM 4
|
||||
|
||||
struct sunplus_pwm {
|
||||
struct pwm_chip chip;
|
||||
void __iomem *base;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
static inline struct sunplus_pwm *to_sunplus_pwm(struct pwm_chip *chip)
|
||||
{
|
||||
return container_of(chip, struct sunplus_pwm, chip);
|
||||
}
|
||||
|
||||
static int sunplus_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
const struct pwm_state *state)
|
||||
{
|
||||
struct sunplus_pwm *priv = to_sunplus_pwm(chip);
|
||||
u32 dd_freq, duty, mode0, mode1;
|
||||
u64 clk_rate;
|
||||
|
||||
if (state->polarity != pwm->state.polarity)
|
||||
return -EINVAL;
|
||||
|
||||
if (!state->enabled) {
|
||||
/* disable pwm channel output */
|
||||
mode0 = readl(priv->base + SP7021_PWM_MODE0);
|
||||
mode0 &= ~SP7021_PWM_MODE0_PWMEN(pwm->hwpwm);
|
||||
writel(mode0, priv->base + SP7021_PWM_MODE0);
|
||||
/* disable pwm channel clk source */
|
||||
mode1 = readl(priv->base + SP7021_PWM_MODE1);
|
||||
mode1 &= ~SP7021_PWM_MODE1_CNT_EN(pwm->hwpwm);
|
||||
writel(mode1, priv->base + SP7021_PWM_MODE1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
clk_rate = clk_get_rate(priv->clk);
|
||||
|
||||
/*
|
||||
* The following calculations might overflow if clk is bigger
|
||||
* than 256 GHz. In practise it's 202.5MHz, so this limitation
|
||||
* is only theoretic.
|
||||
*/
|
||||
if (clk_rate > (u64)SP7021_PWM_FREQ_SCALER * NSEC_PER_SEC)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* With clk_rate limited above we have dd_freq <= state->period,
|
||||
* so this cannot overflow.
|
||||
*/
|
||||
dd_freq = mul_u64_u64_div_u64(clk_rate, state->period, (u64)SP7021_PWM_FREQ_SCALER
|
||||
* NSEC_PER_SEC);
|
||||
|
||||
if (dd_freq == 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (dd_freq > SP7021_PWM_FREQ_MAX)
|
||||
dd_freq = SP7021_PWM_FREQ_MAX;
|
||||
|
||||
writel(dd_freq, priv->base + SP7021_PWM_FREQ(pwm->hwpwm));
|
||||
|
||||
/* cal and set pwm duty */
|
||||
mode0 = readl(priv->base + SP7021_PWM_MODE0);
|
||||
mode0 |= SP7021_PWM_MODE0_PWMEN(pwm->hwpwm);
|
||||
mode1 = readl(priv->base + SP7021_PWM_MODE1);
|
||||
mode1 |= SP7021_PWM_MODE1_CNT_EN(pwm->hwpwm);
|
||||
if (state->duty_cycle == state->period) {
|
||||
/* PWM channel output = high */
|
||||
mode0 |= SP7021_PWM_MODE0_BYPASS(pwm->hwpwm);
|
||||
duty = SP7021_PWM_DUTY_DD_SEL(pwm->hwpwm) | SP7021_PWM_DUTY_MAX;
|
||||
} else {
|
||||
mode0 &= ~SP7021_PWM_MODE0_BYPASS(pwm->hwpwm);
|
||||
/*
|
||||
* duty_ns <= period_ns 27 bits, clk_rate 28 bits, won't overflow.
|
||||
*/
|
||||
duty = mul_u64_u64_div_u64(state->duty_cycle, clk_rate,
|
||||
(u64)dd_freq * NSEC_PER_SEC);
|
||||
duty = SP7021_PWM_DUTY_DD_SEL(pwm->hwpwm) | duty;
|
||||
}
|
||||
writel(duty, priv->base + SP7021_PWM_DUTY(pwm->hwpwm));
|
||||
writel(mode1, priv->base + SP7021_PWM_MODE1);
|
||||
writel(mode0, priv->base + SP7021_PWM_MODE0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sunplus_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
struct pwm_state *state)
|
||||
{
|
||||
struct sunplus_pwm *priv = to_sunplus_pwm(chip);
|
||||
u32 mode0, dd_freq, duty;
|
||||
u64 clk_rate;
|
||||
|
||||
mode0 = readl(priv->base + SP7021_PWM_MODE0);
|
||||
|
||||
if (mode0 & BIT(pwm->hwpwm)) {
|
||||
clk_rate = clk_get_rate(priv->clk);
|
||||
dd_freq = readl(priv->base + SP7021_PWM_FREQ(pwm->hwpwm));
|
||||
duty = readl(priv->base + SP7021_PWM_DUTY(pwm->hwpwm));
|
||||
duty = FIELD_GET(SP7021_PWM_DUTY_MASK, duty);
|
||||
/*
|
||||
* dd_freq 16 bits, SP7021_PWM_FREQ_SCALER 8 bits
|
||||
* NSEC_PER_SEC 30 bits, won't overflow.
|
||||
*/
|
||||
state->period = DIV64_U64_ROUND_UP((u64)dd_freq * (u64)SP7021_PWM_FREQ_SCALER
|
||||
* NSEC_PER_SEC, clk_rate);
|
||||
/*
|
||||
* dd_freq 16 bits, duty 8 bits, NSEC_PER_SEC 30 bits, won't overflow.
|
||||
*/
|
||||
state->duty_cycle = DIV64_U64_ROUND_UP((u64)dd_freq * (u64)duty * NSEC_PER_SEC,
|
||||
clk_rate);
|
||||
state->enabled = true;
|
||||
} else {
|
||||
state->enabled = false;
|
||||
}
|
||||
|
||||
state->polarity = PWM_POLARITY_NORMAL;
|
||||
}
|
||||
|
||||
static const struct pwm_ops sunplus_pwm_ops = {
|
||||
.apply = sunplus_pwm_apply,
|
||||
.get_state = sunplus_pwm_get_state,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static void sunplus_pwm_clk_release(void *data)
|
||||
{
|
||||
struct clk *clk = data;
|
||||
|
||||
clk_disable_unprepare(clk);
|
||||
}
|
||||
|
||||
static int sunplus_pwm_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct sunplus_pwm *priv;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(priv->base))
|
||||
return PTR_ERR(priv->base);
|
||||
|
||||
priv->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(priv->clk))
|
||||
return dev_err_probe(dev, PTR_ERR(priv->clk),
|
||||
"get pwm clock failed\n");
|
||||
|
||||
ret = clk_prepare_enable(priv->clk);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to enable clock: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_add_action_or_reset(dev, sunplus_pwm_clk_release, priv->clk);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to release clock: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
priv->chip.dev = dev;
|
||||
priv->chip.ops = &sunplus_pwm_ops;
|
||||
priv->chip.npwm = SP7021_PWM_NUM;
|
||||
|
||||
ret = devm_pwmchip_add(dev, &priv->chip);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(dev, ret, "Cannot register sunplus PWM\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id sunplus_pwm_of_match[] = {
|
||||
{ .compatible = "sunplus,sp7021-pwm", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sunplus_pwm_of_match);
|
||||
|
||||
static struct platform_driver sunplus_pwm_driver = {
|
||||
.probe = sunplus_pwm_probe,
|
||||
.driver = {
|
||||
.name = "sunplus-pwm",
|
||||
.of_match_table = sunplus_pwm_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(sunplus_pwm_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Sunplus SoC PWM Driver");
|
||||
MODULE_AUTHOR("Hammer Hsieh <hammerh0314@gmail.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -99,7 +99,7 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
|
|||
int duty_ns, int period_ns)
|
||||
{
|
||||
struct tegra_pwm_chip *pc = to_tegra_pwm_chip(chip);
|
||||
unsigned long long c = duty_ns, hz;
|
||||
unsigned long long c = duty_ns;
|
||||
unsigned long rate, required_clk_rate;
|
||||
u32 val = 0;
|
||||
int err;
|
||||
|
|
@ -156,11 +156,9 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
|
|||
pc->clk_rate = clk_get_rate(pc->clk);
|
||||
}
|
||||
|
||||
rate = pc->clk_rate >> PWM_DUTY_WIDTH;
|
||||
|
||||
/* Consider precision in PWM_SCALE_WIDTH rate calculation */
|
||||
hz = DIV_ROUND_CLOSEST_ULL(100ULL * NSEC_PER_SEC, period_ns);
|
||||
rate = DIV_ROUND_CLOSEST_ULL(100ULL * rate, hz);
|
||||
rate = mul_u64_u64_div_u64(pc->clk_rate, period_ns,
|
||||
(u64)NSEC_PER_SEC << PWM_DUTY_WIDTH);
|
||||
|
||||
/*
|
||||
* Since the actual PWM divider is the register's frequency divider
|
||||
|
|
@ -169,6 +167,8 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
|
|||
*/
|
||||
if (rate > 0)
|
||||
rate--;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Make sure that the rate will fit in the register's frequency
|
||||
|
|
@ -230,10 +230,34 @@ static void tegra_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
|
|||
pm_runtime_put_sync(pc->dev);
|
||||
}
|
||||
|
||||
static int tegra_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
const struct pwm_state *state)
|
||||
{
|
||||
int err;
|
||||
bool enabled = pwm->state.enabled;
|
||||
|
||||
if (state->polarity != PWM_POLARITY_NORMAL)
|
||||
return -EINVAL;
|
||||
|
||||
if (!state->enabled) {
|
||||
if (enabled)
|
||||
tegra_pwm_disable(chip, pwm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = tegra_pwm_config(pwm->chip, pwm, state->duty_cycle, state->period);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!enabled)
|
||||
err = tegra_pwm_enable(chip, pwm);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct pwm_ops tegra_pwm_ops = {
|
||||
.config = tegra_pwm_config,
|
||||
.enable = tegra_pwm_enable,
|
||||
.disable = tegra_pwm_disable,
|
||||
.apply = tegra_pwm_apply,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -137,6 +137,45 @@ out:
|
|||
mutex_unlock(&twl->mutex);
|
||||
}
|
||||
|
||||
static int twl4030_pwmled_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
const struct pwm_state *state)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (state->polarity != PWM_POLARITY_NORMAL)
|
||||
return -EINVAL;
|
||||
|
||||
if (!state->enabled) {
|
||||
if (pwm->state.enabled)
|
||||
twl4030_pwmled_disable(chip, pwm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* We cannot skip calling ->config even if state->period ==
|
||||
* pwm->state.period && state->duty_cycle == pwm->state.duty_cycle
|
||||
* because we might have exited early in the last call to
|
||||
* pwm_apply_state because of !state->enabled and so the two values in
|
||||
* pwm->state might not be configured in hardware.
|
||||
*/
|
||||
ret = twl4030_pwmled_config(pwm->chip, pwm,
|
||||
state->duty_cycle, state->period);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!pwm->state.enabled)
|
||||
ret = twl4030_pwmled_enable(chip, pwm);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static const struct pwm_ops twl4030_pwmled_ops = {
|
||||
.apply = twl4030_pwmled_apply,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int twl6030_pwmled_config(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
int duty_ns, int period_ns)
|
||||
{
|
||||
|
|
@ -206,6 +245,32 @@ out:
|
|||
mutex_unlock(&twl->mutex);
|
||||
}
|
||||
|
||||
static int twl6030_pwmled_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
const struct pwm_state *state)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (state->polarity != pwm->state.polarity)
|
||||
return -EINVAL;
|
||||
|
||||
if (!state->enabled) {
|
||||
if (pwm->state.enabled)
|
||||
twl6030_pwmled_disable(chip, pwm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = twl6030_pwmled_config(pwm->chip, pwm,
|
||||
state->duty_cycle, state->period);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!pwm->state.enabled)
|
||||
err = twl6030_pwmled_enable(chip, pwm);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int twl6030_pwmled_request(struct pwm_chip *chip, struct pwm_device *pwm)
|
||||
{
|
||||
struct twl_pwmled_chip *twl = to_twl(chip);
|
||||
|
|
@ -257,17 +322,8 @@ out:
|
|||
mutex_unlock(&twl->mutex);
|
||||
}
|
||||
|
||||
static const struct pwm_ops twl4030_pwmled_ops = {
|
||||
.enable = twl4030_pwmled_enable,
|
||||
.disable = twl4030_pwmled_disable,
|
||||
.config = twl4030_pwmled_config,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct pwm_ops twl6030_pwmled_ops = {
|
||||
.enable = twl6030_pwmled_enable,
|
||||
.disable = twl6030_pwmled_disable,
|
||||
.config = twl6030_pwmled_config,
|
||||
.apply = twl6030_pwmled_apply,
|
||||
.request = twl6030_pwmled_request,
|
||||
.free = twl6030_pwmled_free,
|
||||
.owner = THIS_MODULE,
|
||||
|
|
|
|||
321
drivers/pwm/pwm-xilinx.c
Normal file
321
drivers/pwm/pwm-xilinx.c
Normal file
|
|
@ -0,0 +1,321 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2021 Sean Anderson <sean.anderson@seco.com>
|
||||
*
|
||||
* Limitations:
|
||||
* - When changing both duty cycle and period, we may end up with one cycle
|
||||
* with the old duty cycle and the new period. This is because the counters
|
||||
* may only be reloaded by first stopping them, or by letting them be
|
||||
* automatically reloaded at the end of a cycle. If this automatic reload
|
||||
* happens after we set TLR0 but before we set TLR1 then we will have a
|
||||
* bad cycle. This could probably be fixed by reading TCR0 just before
|
||||
* reprogramming, but I think it would add complexity for little gain.
|
||||
* - Cannot produce 100% duty cycle by configuring the TLRs. This might be
|
||||
* possible by stopping the counters at an appropriate point in the cycle,
|
||||
* but this is not (yet) implemented.
|
||||
* - Only produces "normal" output.
|
||||
* - Always produces low output if disabled.
|
||||
*/
|
||||
|
||||
#include <clocksource/timer-xilinx.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pwm.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
/*
|
||||
* The following functions are "common" to drivers for this device, and may be
|
||||
* exported at a future date.
|
||||
*/
|
||||
u32 xilinx_timer_tlr_cycles(struct xilinx_timer_priv *priv, u32 tcsr,
|
||||
u64 cycles)
|
||||
{
|
||||
WARN_ON(cycles < 2 || cycles - 2 > priv->max);
|
||||
|
||||
if (tcsr & TCSR_UDT)
|
||||
return cycles - 2;
|
||||
return priv->max - cycles + 2;
|
||||
}
|
||||
|
||||
unsigned int xilinx_timer_get_period(struct xilinx_timer_priv *priv,
|
||||
u32 tlr, u32 tcsr)
|
||||
{
|
||||
u64 cycles;
|
||||
|
||||
if (tcsr & TCSR_UDT)
|
||||
cycles = tlr + 2;
|
||||
else
|
||||
cycles = (u64)priv->max - tlr + 2;
|
||||
|
||||
/* cycles has a max of 2^32 + 2, so we can't overflow */
|
||||
return DIV64_U64_ROUND_UP(cycles * NSEC_PER_SEC,
|
||||
clk_get_rate(priv->clk));
|
||||
}
|
||||
|
||||
/*
|
||||
* The idea here is to capture whether the PWM is actually running (e.g.
|
||||
* because we or the bootloader set it up) and we need to be careful to ensure
|
||||
* we don't cause a glitch. According to the data sheet, to enable the PWM we
|
||||
* need to
|
||||
*
|
||||
* - Set both timers to generate mode (MDT=1)
|
||||
* - Set both timers to PWM mode (PWMA=1)
|
||||
* - Enable the generate out signals (GENT=1)
|
||||
*
|
||||
* In addition,
|
||||
*
|
||||
* - The timer must be running (ENT=1)
|
||||
* - The timer must auto-reload TLR into TCR (ARHT=1)
|
||||
* - We must not be in the process of loading TLR into TCR (LOAD=0)
|
||||
* - Cascade mode must be disabled (CASC=0)
|
||||
*
|
||||
* If any of these differ from usual, then the PWM is either disabled, or is
|
||||
* running in a mode that this driver does not support.
|
||||
*/
|
||||
#define TCSR_PWM_SET (TCSR_GENT | TCSR_ARHT | TCSR_ENT | TCSR_PWMA)
|
||||
#define TCSR_PWM_CLEAR (TCSR_MDT | TCSR_LOAD)
|
||||
#define TCSR_PWM_MASK (TCSR_PWM_SET | TCSR_PWM_CLEAR)
|
||||
|
||||
struct xilinx_pwm_device {
|
||||
struct pwm_chip chip;
|
||||
struct xilinx_timer_priv priv;
|
||||
};
|
||||
|
||||
static inline struct xilinx_timer_priv
|
||||
*xilinx_pwm_chip_to_priv(struct pwm_chip *chip)
|
||||
{
|
||||
return &container_of(chip, struct xilinx_pwm_device, chip)->priv;
|
||||
}
|
||||
|
||||
static bool xilinx_timer_pwm_enabled(u32 tcsr0, u32 tcsr1)
|
||||
{
|
||||
return ((TCSR_PWM_MASK | TCSR_CASC) & tcsr0) == TCSR_PWM_SET &&
|
||||
(TCSR_PWM_MASK & tcsr1) == TCSR_PWM_SET;
|
||||
}
|
||||
|
||||
static int xilinx_pwm_apply(struct pwm_chip *chip, struct pwm_device *unused,
|
||||
const struct pwm_state *state)
|
||||
{
|
||||
struct xilinx_timer_priv *priv = xilinx_pwm_chip_to_priv(chip);
|
||||
u32 tlr0, tlr1, tcsr0, tcsr1;
|
||||
u64 period_cycles, duty_cycles;
|
||||
unsigned long rate;
|
||||
|
||||
if (state->polarity != PWM_POLARITY_NORMAL)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* To be representable by TLR, cycles must be between 2 and
|
||||
* priv->max + 2. To enforce this we can reduce the cycles, but we may
|
||||
* not increase them. Caveat emptor: while this does result in more
|
||||
* predictable rounding, it may also result in a completely different
|
||||
* duty cycle (% high time) than what was requested.
|
||||
*/
|
||||
rate = clk_get_rate(priv->clk);
|
||||
/* Avoid overflow */
|
||||
period_cycles = min_t(u64, state->period, U32_MAX * NSEC_PER_SEC);
|
||||
period_cycles = mul_u64_u32_div(period_cycles, rate, NSEC_PER_SEC);
|
||||
period_cycles = min_t(u64, period_cycles, priv->max + 2);
|
||||
if (period_cycles < 2)
|
||||
return -ERANGE;
|
||||
|
||||
/* Same thing for duty cycles */
|
||||
duty_cycles = min_t(u64, state->duty_cycle, U32_MAX * NSEC_PER_SEC);
|
||||
duty_cycles = mul_u64_u32_div(duty_cycles, rate, NSEC_PER_SEC);
|
||||
duty_cycles = min_t(u64, duty_cycles, priv->max + 2);
|
||||
|
||||
/*
|
||||
* If we specify 100% duty cycle, we will get 0% instead, so decrease
|
||||
* the duty cycle count by one.
|
||||
*/
|
||||
if (duty_cycles >= period_cycles)
|
||||
duty_cycles = period_cycles - 1;
|
||||
|
||||
/* Round down to 0% duty cycle for unrepresentable duty cycles */
|
||||
if (duty_cycles < 2)
|
||||
duty_cycles = period_cycles;
|
||||
|
||||
regmap_read(priv->map, TCSR0, &tcsr0);
|
||||
regmap_read(priv->map, TCSR1, &tcsr1);
|
||||
tlr0 = xilinx_timer_tlr_cycles(priv, tcsr0, period_cycles);
|
||||
tlr1 = xilinx_timer_tlr_cycles(priv, tcsr1, duty_cycles);
|
||||
regmap_write(priv->map, TLR0, tlr0);
|
||||
regmap_write(priv->map, TLR1, tlr1);
|
||||
|
||||
if (state->enabled) {
|
||||
/*
|
||||
* If the PWM is already running, then the counters will be
|
||||
* reloaded at the end of the current cycle.
|
||||
*/
|
||||
if (!xilinx_timer_pwm_enabled(tcsr0, tcsr1)) {
|
||||
/* Load TLR into TCR */
|
||||
regmap_write(priv->map, TCSR0, tcsr0 | TCSR_LOAD);
|
||||
regmap_write(priv->map, TCSR1, tcsr1 | TCSR_LOAD);
|
||||
/* Enable timers all at once with ENALL */
|
||||
tcsr0 = (TCSR_PWM_SET & ~TCSR_ENT) | (tcsr0 & TCSR_UDT);
|
||||
tcsr1 = TCSR_PWM_SET | TCSR_ENALL | (tcsr1 & TCSR_UDT);
|
||||
regmap_write(priv->map, TCSR0, tcsr0);
|
||||
regmap_write(priv->map, TCSR1, tcsr1);
|
||||
}
|
||||
} else {
|
||||
regmap_write(priv->map, TCSR0, 0);
|
||||
regmap_write(priv->map, TCSR1, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void xilinx_pwm_get_state(struct pwm_chip *chip,
|
||||
struct pwm_device *unused,
|
||||
struct pwm_state *state)
|
||||
{
|
||||
struct xilinx_timer_priv *priv = xilinx_pwm_chip_to_priv(chip);
|
||||
u32 tlr0, tlr1, tcsr0, tcsr1;
|
||||
|
||||
regmap_read(priv->map, TLR0, &tlr0);
|
||||
regmap_read(priv->map, TLR1, &tlr1);
|
||||
regmap_read(priv->map, TCSR0, &tcsr0);
|
||||
regmap_read(priv->map, TCSR1, &tcsr1);
|
||||
state->period = xilinx_timer_get_period(priv, tlr0, tcsr0);
|
||||
state->duty_cycle = xilinx_timer_get_period(priv, tlr1, tcsr1);
|
||||
state->enabled = xilinx_timer_pwm_enabled(tcsr0, tcsr1);
|
||||
state->polarity = PWM_POLARITY_NORMAL;
|
||||
|
||||
/*
|
||||
* 100% duty cycle results in constant low output. This may be (very)
|
||||
* wrong if rate > 1 GHz, so fix this if you have such hardware :)
|
||||
*/
|
||||
if (state->period == state->duty_cycle)
|
||||
state->duty_cycle = 0;
|
||||
}
|
||||
|
||||
static const struct pwm_ops xilinx_pwm_ops = {
|
||||
.apply = xilinx_pwm_apply,
|
||||
.get_state = xilinx_pwm_get_state,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct regmap_config xilinx_pwm_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.val_bits = 32,
|
||||
.val_format_endian = REGMAP_ENDIAN_LITTLE,
|
||||
.max_register = TCR1,
|
||||
};
|
||||
|
||||
static int xilinx_pwm_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct xilinx_timer_priv *priv;
|
||||
struct xilinx_pwm_device *xilinx_pwm;
|
||||
u32 pwm_cells, one_timer, width;
|
||||
void __iomem *regs;
|
||||
|
||||
/* If there are no PWM cells, this binding is for a timer */
|
||||
ret = of_property_read_u32(np, "#pwm-cells", &pwm_cells);
|
||||
if (ret == -EINVAL)
|
||||
return -ENODEV;
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "could not read #pwm-cells\n");
|
||||
|
||||
xilinx_pwm = devm_kzalloc(dev, sizeof(*xilinx_pwm), GFP_KERNEL);
|
||||
if (!xilinx_pwm)
|
||||
return -ENOMEM;
|
||||
platform_set_drvdata(pdev, xilinx_pwm);
|
||||
priv = &xilinx_pwm->priv;
|
||||
|
||||
regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(regs))
|
||||
return PTR_ERR(regs);
|
||||
|
||||
priv->map = devm_regmap_init_mmio(dev, regs,
|
||||
&xilinx_pwm_regmap_config);
|
||||
if (IS_ERR(priv->map))
|
||||
return dev_err_probe(dev, PTR_ERR(priv->map),
|
||||
"Could not create regmap\n");
|
||||
|
||||
ret = of_property_read_u32(np, "xlnx,one-timer-only", &one_timer);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret,
|
||||
"Could not read xlnx,one-timer-only\n");
|
||||
|
||||
if (one_timer)
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"Two timers required for PWM mode\n");
|
||||
|
||||
ret = of_property_read_u32(np, "xlnx,count-width", &width);
|
||||
if (ret == -EINVAL)
|
||||
width = 32;
|
||||
else if (ret)
|
||||
return dev_err_probe(dev, ret,
|
||||
"Could not read xlnx,count-width\n");
|
||||
|
||||
if (width != 8 && width != 16 && width != 32)
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"Invalid counter width %d\n", width);
|
||||
priv->max = BIT_ULL(width) - 1;
|
||||
|
||||
/*
|
||||
* The polarity of the Generate Out signals must be active high for PWM
|
||||
* mode to work. We could determine this from the device tree, but
|
||||
* alas, such properties are not allowed to be used.
|
||||
*/
|
||||
|
||||
priv->clk = devm_clk_get(dev, "s_axi_aclk");
|
||||
if (IS_ERR(priv->clk))
|
||||
return dev_err_probe(dev, PTR_ERR(priv->clk),
|
||||
"Could not get clock\n");
|
||||
|
||||
ret = clk_prepare_enable(priv->clk);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Clock enable failed\n");
|
||||
clk_rate_exclusive_get(priv->clk);
|
||||
|
||||
xilinx_pwm->chip.dev = dev;
|
||||
xilinx_pwm->chip.ops = &xilinx_pwm_ops;
|
||||
xilinx_pwm->chip.npwm = 1;
|
||||
ret = pwmchip_add(&xilinx_pwm->chip);
|
||||
if (ret) {
|
||||
clk_rate_exclusive_put(priv->clk);
|
||||
clk_disable_unprepare(priv->clk);
|
||||
return dev_err_probe(dev, ret, "Could not register PWM chip\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xilinx_pwm_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct xilinx_pwm_device *xilinx_pwm = platform_get_drvdata(pdev);
|
||||
|
||||
pwmchip_remove(&xilinx_pwm->chip);
|
||||
clk_rate_exclusive_put(xilinx_pwm->priv.clk);
|
||||
clk_disable_unprepare(xilinx_pwm->priv.clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id xilinx_pwm_of_match[] = {
|
||||
{ .compatible = "xlnx,xps-timer-1.00.a", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, xilinx_pwm_of_match);
|
||||
|
||||
static struct platform_driver xilinx_pwm_driver = {
|
||||
.probe = xilinx_pwm_probe,
|
||||
.remove = xilinx_pwm_remove,
|
||||
.driver = {
|
||||
.name = "xilinx-pwm",
|
||||
.of_match_table = of_match_ptr(xilinx_pwm_of_match),
|
||||
},
|
||||
};
|
||||
module_platform_driver(xilinx_pwm_driver);
|
||||
|
||||
MODULE_ALIAS("platform:xilinx-pwm");
|
||||
MODULE_DESCRIPTION("PWM driver for Xilinx LogiCORE IP AXI Timer");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -649,99 +649,6 @@ static int imx_dsp_rproc_add_carveout(struct imx_dsp_rproc *priv)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* imx_dsp_rproc_elf_load_segments() - load firmware segments to memory
|
||||
* @rproc: remote processor which will be booted using these fw segments
|
||||
* @fw: the ELF firmware image
|
||||
*
|
||||
* This function specially checks if memsz is zero or not, otherwise it
|
||||
* is mostly same as rproc_elf_load_segments().
|
||||
*/
|
||||
static int imx_dsp_rproc_elf_load_segments(struct rproc *rproc,
|
||||
const struct firmware *fw)
|
||||
{
|
||||
struct device *dev = &rproc->dev;
|
||||
u8 class = fw_elf_get_class(fw);
|
||||
u32 elf_phdr_get_size = elf_size_of_phdr(class);
|
||||
const u8 *elf_data = fw->data;
|
||||
const void *ehdr, *phdr;
|
||||
int i, ret = 0;
|
||||
u16 phnum;
|
||||
|
||||
ehdr = elf_data;
|
||||
phnum = elf_hdr_get_e_phnum(class, ehdr);
|
||||
phdr = elf_data + elf_hdr_get_e_phoff(class, ehdr);
|
||||
|
||||
/* go through the available ELF segments */
|
||||
for (i = 0; i < phnum; i++, phdr += elf_phdr_get_size) {
|
||||
u64 da = elf_phdr_get_p_paddr(class, phdr);
|
||||
u64 memsz = elf_phdr_get_p_memsz(class, phdr);
|
||||
u64 filesz = elf_phdr_get_p_filesz(class, phdr);
|
||||
u64 offset = elf_phdr_get_p_offset(class, phdr);
|
||||
u32 type = elf_phdr_get_p_type(class, phdr);
|
||||
void *ptr;
|
||||
|
||||
/*
|
||||
* There is a case that with PT_LOAD type, the
|
||||
* filesz = memsz = 0. If memsz = 0, rproc_da_to_va
|
||||
* should return NULL ptr, then error is returned.
|
||||
* So this case should be skipped from the loop.
|
||||
* Add !memsz checking here.
|
||||
*/
|
||||
if (type != PT_LOAD || !memsz)
|
||||
continue;
|
||||
|
||||
dev_dbg(dev, "phdr: type %d da 0x%llx memsz 0x%llx filesz 0x%llx\n",
|
||||
type, da, memsz, filesz);
|
||||
|
||||
if (filesz > memsz) {
|
||||
dev_err(dev, "bad phdr filesz 0x%llx memsz 0x%llx\n",
|
||||
filesz, memsz);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (offset + filesz > fw->size) {
|
||||
dev_err(dev, "truncated fw: need 0x%llx avail 0x%zx\n",
|
||||
offset + filesz, fw->size);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!rproc_u64_fit_in_size_t(memsz)) {
|
||||
dev_err(dev, "size (%llx) does not fit in size_t type\n",
|
||||
memsz);
|
||||
ret = -EOVERFLOW;
|
||||
break;
|
||||
}
|
||||
|
||||
/* grab the kernel address for this device address */
|
||||
ptr = rproc_da_to_va(rproc, da, memsz, NULL);
|
||||
if (!ptr) {
|
||||
dev_err(dev, "bad phdr da 0x%llx mem 0x%llx\n", da,
|
||||
memsz);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
/* put the segment where the remote processor expects it */
|
||||
if (filesz)
|
||||
memcpy(ptr, elf_data + offset, filesz);
|
||||
|
||||
/*
|
||||
* Zero out remaining memory for this segment.
|
||||
*
|
||||
* This isn't strictly required since dma_alloc_coherent already
|
||||
* did this for us. albeit harmless, we may consider removing
|
||||
* this.
|
||||
*/
|
||||
if (memsz > filesz)
|
||||
memset(ptr + filesz, 0, memsz - filesz);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Prepare function for rproc_ops */
|
||||
static int imx_dsp_rproc_prepare(struct rproc *rproc)
|
||||
{
|
||||
|
|
@ -802,14 +709,22 @@ static void imx_dsp_rproc_kick(struct rproc *rproc, int vqid)
|
|||
dev_err(dev, "%s: failed (%d, err:%d)\n", __func__, vqid, err);
|
||||
}
|
||||
|
||||
static int imx_dsp_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw)
|
||||
{
|
||||
if (rproc_elf_load_rsc_table(rproc, fw))
|
||||
dev_warn(&rproc->dev, "no resource table found for this firmware\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct rproc_ops imx_dsp_rproc_ops = {
|
||||
.prepare = imx_dsp_rproc_prepare,
|
||||
.unprepare = imx_dsp_rproc_unprepare,
|
||||
.start = imx_dsp_rproc_start,
|
||||
.stop = imx_dsp_rproc_stop,
|
||||
.kick = imx_dsp_rproc_kick,
|
||||
.load = imx_dsp_rproc_elf_load_segments,
|
||||
.parse_fw = rproc_elf_load_rsc_table,
|
||||
.load = rproc_elf_load_segments,
|
||||
.parse_fw = imx_dsp_rproc_parse_fw,
|
||||
.sanity_check = rproc_elf_sanity_check,
|
||||
.get_boot_addr = rproc_elf_get_boot_addr,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -91,6 +91,32 @@ struct imx_rproc {
|
|||
void __iomem *rsc_table;
|
||||
};
|
||||
|
||||
static const struct imx_rproc_att imx_rproc_att_imx93[] = {
|
||||
/* dev addr , sys addr , size , flags */
|
||||
/* TCM CODE NON-SECURE */
|
||||
{ 0x0FFC0000, 0x201C0000, 0x00020000, ATT_OWN | ATT_IOMEM },
|
||||
{ 0x0FFE0000, 0x201E0000, 0x00020000, ATT_OWN | ATT_IOMEM },
|
||||
|
||||
/* TCM CODE SECURE */
|
||||
{ 0x1FFC0000, 0x201C0000, 0x00020000, ATT_OWN | ATT_IOMEM },
|
||||
{ 0x1FFE0000, 0x201E0000, 0x00020000, ATT_OWN | ATT_IOMEM },
|
||||
|
||||
/* TCM SYS NON-SECURE*/
|
||||
{ 0x20000000, 0x20200000, 0x00020000, ATT_OWN | ATT_IOMEM },
|
||||
{ 0x20020000, 0x20220000, 0x00020000, ATT_OWN | ATT_IOMEM },
|
||||
|
||||
/* TCM SYS SECURE*/
|
||||
{ 0x30000000, 0x20200000, 0x00020000, ATT_OWN | ATT_IOMEM },
|
||||
{ 0x30020000, 0x20220000, 0x00020000, ATT_OWN | ATT_IOMEM },
|
||||
|
||||
/* DDR */
|
||||
{ 0x80000000, 0x80000000, 0x10000000, 0 },
|
||||
{ 0x90000000, 0x80000000, 0x10000000, 0 },
|
||||
|
||||
{ 0xC0000000, 0xa0000000, 0x10000000, 0 },
|
||||
{ 0xD0000000, 0xa0000000, 0x10000000, 0 },
|
||||
};
|
||||
|
||||
static const struct imx_rproc_att imx_rproc_att_imx8mn[] = {
|
||||
/* dev addr , sys addr , size , flags */
|
||||
/* ITCM */
|
||||
|
|
@ -261,6 +287,12 @@ static const struct imx_rproc_dcfg imx_rproc_cfg_imx6sx = {
|
|||
.method = IMX_RPROC_MMIO,
|
||||
};
|
||||
|
||||
static const struct imx_rproc_dcfg imx_rproc_cfg_imx93 = {
|
||||
.att = imx_rproc_att_imx93,
|
||||
.att_size = ARRAY_SIZE(imx_rproc_att_imx93),
|
||||
.method = IMX_RPROC_SMC,
|
||||
};
|
||||
|
||||
static int imx_rproc_start(struct rproc *rproc)
|
||||
{
|
||||
struct imx_rproc *priv = rproc->priv;
|
||||
|
|
@ -423,6 +455,9 @@ static int imx_rproc_prepare(struct rproc *rproc)
|
|||
if (!strcmp(it.node->name, "vdev0buffer"))
|
||||
continue;
|
||||
|
||||
if (!strcmp(it.node->name, "rsc-table"))
|
||||
continue;
|
||||
|
||||
rmem = of_reserved_mem_lookup(it.node);
|
||||
if (!rmem) {
|
||||
dev_err(priv->dev, "unable to acquire memory-region\n");
|
||||
|
|
@ -821,6 +856,7 @@ static const struct of_device_id imx_rproc_of_match[] = {
|
|||
{ .compatible = "fsl,imx8mn-cm7", .data = &imx_rproc_cfg_imx8mn },
|
||||
{ .compatible = "fsl,imx8mp-cm7", .data = &imx_rproc_cfg_imx8mn },
|
||||
{ .compatible = "fsl,imx8ulp-cm33", .data = &imx_rproc_cfg_imx8ulp },
|
||||
{ .compatible = "fsl,imx93-cm33", .data = &imx_rproc_cfg_imx93 },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, imx_rproc_of_match);
|
||||
|
|
|
|||
|
|
@ -54,6 +54,8 @@
|
|||
#define MT8192_CORE0_WDT_IRQ 0x10030
|
||||
#define MT8192_CORE0_WDT_CFG 0x10034
|
||||
|
||||
#define MT8195_L1TCM_SRAM_PDN_RESERVED_RSI_BITS GENMASK(7, 4)
|
||||
|
||||
#define SCP_FW_VER_LEN 32
|
||||
#define SCP_SHARE_BUFFER_SIZE 288
|
||||
|
||||
|
|
|
|||
|
|
@ -365,22 +365,22 @@ static int mt8183_scp_before_load(struct mtk_scp *scp)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void mt8192_power_on_sram(void __iomem *addr)
|
||||
static void scp_sram_power_on(void __iomem *addr, u32 reserved_mask)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 31; i >= 0; i--)
|
||||
writel(GENMASK(i, 0), addr);
|
||||
writel(GENMASK(i, 0) & ~reserved_mask, addr);
|
||||
writel(0, addr);
|
||||
}
|
||||
|
||||
static void mt8192_power_off_sram(void __iomem *addr)
|
||||
static void scp_sram_power_off(void __iomem *addr, u32 reserved_mask)
|
||||
{
|
||||
int i;
|
||||
|
||||
writel(0, addr);
|
||||
for (i = 0; i < 32; i++)
|
||||
writel(GENMASK(i, 0), addr);
|
||||
writel(GENMASK(i, 0) & ~reserved_mask, addr);
|
||||
}
|
||||
|
||||
static int mt8186_scp_before_load(struct mtk_scp *scp)
|
||||
|
|
@ -393,7 +393,7 @@ static int mt8186_scp_before_load(struct mtk_scp *scp)
|
|||
writel(0x0, scp->reg_base + MT8183_SCP_CLK_DIV_SEL);
|
||||
|
||||
/* Turn on the power of SCP's SRAM before using it. Enable 1 block per time*/
|
||||
mt8192_power_on_sram(scp->reg_base + MT8183_SCP_SRAM_PDN);
|
||||
scp_sram_power_on(scp->reg_base + MT8183_SCP_SRAM_PDN, 0);
|
||||
|
||||
/* Initialize TCM before loading FW. */
|
||||
writel(0x0, scp->reg_base + MT8183_SCP_L1_SRAM_PD);
|
||||
|
|
@ -412,11 +412,32 @@ static int mt8192_scp_before_load(struct mtk_scp *scp)
|
|||
writel(1, scp->reg_base + MT8192_CORE0_SW_RSTN_SET);
|
||||
|
||||
/* enable SRAM clock */
|
||||
mt8192_power_on_sram(scp->reg_base + MT8192_L2TCM_SRAM_PD_0);
|
||||
mt8192_power_on_sram(scp->reg_base + MT8192_L2TCM_SRAM_PD_1);
|
||||
mt8192_power_on_sram(scp->reg_base + MT8192_L2TCM_SRAM_PD_2);
|
||||
mt8192_power_on_sram(scp->reg_base + MT8192_L1TCM_SRAM_PDN);
|
||||
mt8192_power_on_sram(scp->reg_base + MT8192_CPU0_SRAM_PD);
|
||||
scp_sram_power_on(scp->reg_base + MT8192_L2TCM_SRAM_PD_0, 0);
|
||||
scp_sram_power_on(scp->reg_base + MT8192_L2TCM_SRAM_PD_1, 0);
|
||||
scp_sram_power_on(scp->reg_base + MT8192_L2TCM_SRAM_PD_2, 0);
|
||||
scp_sram_power_on(scp->reg_base + MT8192_L1TCM_SRAM_PDN, 0);
|
||||
scp_sram_power_on(scp->reg_base + MT8192_CPU0_SRAM_PD, 0);
|
||||
|
||||
/* enable MPU for all memory regions */
|
||||
writel(0xff, scp->reg_base + MT8192_CORE0_MEM_ATT_PREDEF);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt8195_scp_before_load(struct mtk_scp *scp)
|
||||
{
|
||||
/* clear SPM interrupt, SCP2SPM_IPC_CLR */
|
||||
writel(0xff, scp->reg_base + MT8192_SCP2SPM_IPC_CLR);
|
||||
|
||||
writel(1, scp->reg_base + MT8192_CORE0_SW_RSTN_SET);
|
||||
|
||||
/* enable SRAM clock */
|
||||
scp_sram_power_on(scp->reg_base + MT8192_L2TCM_SRAM_PD_0, 0);
|
||||
scp_sram_power_on(scp->reg_base + MT8192_L2TCM_SRAM_PD_1, 0);
|
||||
scp_sram_power_on(scp->reg_base + MT8192_L2TCM_SRAM_PD_2, 0);
|
||||
scp_sram_power_on(scp->reg_base + MT8192_L1TCM_SRAM_PDN,
|
||||
MT8195_L1TCM_SRAM_PDN_RESERVED_RSI_BITS);
|
||||
scp_sram_power_on(scp->reg_base + MT8192_CPU0_SRAM_PD, 0);
|
||||
|
||||
/* enable MPU for all memory regions */
|
||||
writel(0xff, scp->reg_base + MT8192_CORE0_MEM_ATT_PREDEF);
|
||||
|
|
@ -572,11 +593,25 @@ static void mt8183_scp_stop(struct mtk_scp *scp)
|
|||
static void mt8192_scp_stop(struct mtk_scp *scp)
|
||||
{
|
||||
/* Disable SRAM clock */
|
||||
mt8192_power_off_sram(scp->reg_base + MT8192_L2TCM_SRAM_PD_0);
|
||||
mt8192_power_off_sram(scp->reg_base + MT8192_L2TCM_SRAM_PD_1);
|
||||
mt8192_power_off_sram(scp->reg_base + MT8192_L2TCM_SRAM_PD_2);
|
||||
mt8192_power_off_sram(scp->reg_base + MT8192_L1TCM_SRAM_PDN);
|
||||
mt8192_power_off_sram(scp->reg_base + MT8192_CPU0_SRAM_PD);
|
||||
scp_sram_power_off(scp->reg_base + MT8192_L2TCM_SRAM_PD_0, 0);
|
||||
scp_sram_power_off(scp->reg_base + MT8192_L2TCM_SRAM_PD_1, 0);
|
||||
scp_sram_power_off(scp->reg_base + MT8192_L2TCM_SRAM_PD_2, 0);
|
||||
scp_sram_power_off(scp->reg_base + MT8192_L1TCM_SRAM_PDN, 0);
|
||||
scp_sram_power_off(scp->reg_base + MT8192_CPU0_SRAM_PD, 0);
|
||||
|
||||
/* Disable SCP watchdog */
|
||||
writel(0, scp->reg_base + MT8192_CORE0_WDT_CFG);
|
||||
}
|
||||
|
||||
static void mt8195_scp_stop(struct mtk_scp *scp)
|
||||
{
|
||||
/* Disable SRAM clock */
|
||||
scp_sram_power_off(scp->reg_base + MT8192_L2TCM_SRAM_PD_0, 0);
|
||||
scp_sram_power_off(scp->reg_base + MT8192_L2TCM_SRAM_PD_1, 0);
|
||||
scp_sram_power_off(scp->reg_base + MT8192_L2TCM_SRAM_PD_2, 0);
|
||||
scp_sram_power_off(scp->reg_base + MT8192_L1TCM_SRAM_PDN,
|
||||
MT8195_L1TCM_SRAM_PDN_RESERVED_RSI_BITS);
|
||||
scp_sram_power_off(scp->reg_base + MT8192_CPU0_SRAM_PD, 0);
|
||||
|
||||
/* Disable SCP watchdog */
|
||||
writel(0, scp->reg_base + MT8192_CORE0_WDT_CFG);
|
||||
|
|
@ -774,9 +809,13 @@ static int scp_probe(struct platform_device *pdev)
|
|||
struct mtk_scp *scp;
|
||||
struct rproc *rproc;
|
||||
struct resource *res;
|
||||
char *fw_name = "scp.img";
|
||||
const char *fw_name = "scp.img";
|
||||
int ret, i;
|
||||
|
||||
ret = rproc_of_parse_firmware(dev, 0, &fw_name);
|
||||
if (ret < 0 && ret != -EINVAL)
|
||||
return ret;
|
||||
|
||||
rproc = devm_rproc_alloc(dev, np->name, &scp_ops, fw_name, sizeof(*scp));
|
||||
if (!rproc)
|
||||
return dev_err_probe(dev, -ENOMEM, "unable to allocate remoteproc\n");
|
||||
|
|
@ -877,7 +916,6 @@ static int scp_remove(struct platform_device *pdev)
|
|||
for (i = 0; i < SCP_IPI_MAX; i++)
|
||||
mutex_destroy(&scp->ipi_desc[i].lock);
|
||||
mutex_destroy(&scp->send_lock);
|
||||
rproc_free(scp->rproc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -922,11 +960,11 @@ static const struct mtk_scp_of_data mt8192_of_data = {
|
|||
|
||||
static const struct mtk_scp_of_data mt8195_of_data = {
|
||||
.scp_clk_get = mt8195_scp_clk_get,
|
||||
.scp_before_load = mt8192_scp_before_load,
|
||||
.scp_before_load = mt8195_scp_before_load,
|
||||
.scp_irq_handler = mt8192_scp_irq_handler,
|
||||
.scp_reset_assert = mt8192_scp_reset_assert,
|
||||
.scp_reset_deassert = mt8192_scp_reset_deassert,
|
||||
.scp_stop = mt8192_scp_stop,
|
||||
.scp_stop = mt8195_scp_stop,
|
||||
.scp_da_to_va = mt8192_scp_da_to_va,
|
||||
.host_to_scp_reg = MT8192_GIPC_IN_SET,
|
||||
.host_to_scp_int_bit = MT8192_HOST_IPC_INT_BIT,
|
||||
|
|
|
|||
|
|
@ -704,6 +704,36 @@ static const struct adsp_data sm8250_cdsp_resource = {
|
|||
.ssctl_id = 0x17,
|
||||
};
|
||||
|
||||
static const struct adsp_data sc8280xp_nsp0_resource = {
|
||||
.crash_reason_smem = 601,
|
||||
.firmware_name = "cdsp.mdt",
|
||||
.pas_id = 18,
|
||||
.has_aggre2_clk = false,
|
||||
.auto_boot = true,
|
||||
.proxy_pd_names = (char*[]){
|
||||
"nsp",
|
||||
NULL
|
||||
},
|
||||
.ssr_name = "cdsp0",
|
||||
.sysmon_name = "cdsp",
|
||||
.ssctl_id = 0x17,
|
||||
};
|
||||
|
||||
static const struct adsp_data sc8280xp_nsp1_resource = {
|
||||
.crash_reason_smem = 633,
|
||||
.firmware_name = "cdsp.mdt",
|
||||
.pas_id = 30,
|
||||
.has_aggre2_clk = false,
|
||||
.auto_boot = true,
|
||||
.proxy_pd_names = (char*[]){
|
||||
"nsp",
|
||||
NULL
|
||||
},
|
||||
.ssr_name = "cdsp1",
|
||||
.sysmon_name = "cdsp1",
|
||||
.ssctl_id = 0x20,
|
||||
};
|
||||
|
||||
static const struct adsp_data sm8350_cdsp_resource = {
|
||||
.crash_reason_smem = 601,
|
||||
.firmware_name = "cdsp.mdt",
|
||||
|
|
@ -848,6 +878,7 @@ static const struct adsp_data sdx55_mpss_resource = {
|
|||
};
|
||||
|
||||
static const struct of_device_id adsp_of_match[] = {
|
||||
{ .compatible = "qcom,msm8226-adsp-pil", .data = &adsp_resource_init},
|
||||
{ .compatible = "qcom,msm8974-adsp-pil", .data = &adsp_resource_init},
|
||||
{ .compatible = "qcom,msm8996-adsp-pil", .data = &msm8996_adsp_resource},
|
||||
{ .compatible = "qcom,msm8996-slpi-pil", .data = &slpi_resource_init},
|
||||
|
|
@ -861,6 +892,9 @@ static const struct of_device_id adsp_of_match[] = {
|
|||
{ .compatible = "qcom,sc8180x-adsp-pas", .data = &sm8150_adsp_resource},
|
||||
{ .compatible = "qcom,sc8180x-cdsp-pas", .data = &sm8150_cdsp_resource},
|
||||
{ .compatible = "qcom,sc8180x-mpss-pas", .data = &sc8180x_mpss_resource},
|
||||
{ .compatible = "qcom,sc8280xp-adsp-pas", .data = &sm8250_adsp_resource},
|
||||
{ .compatible = "qcom,sc8280xp-nsp0-pas", .data = &sc8280xp_nsp0_resource},
|
||||
{ .compatible = "qcom,sc8280xp-nsp1-pas", .data = &sc8280xp_nsp1_resource},
|
||||
{ .compatible = "qcom,sdm660-adsp-pas", .data = &adsp_resource_init},
|
||||
{ .compatible = "qcom,sdm845-adsp-pas", .data = &sdm845_adsp_resource_init},
|
||||
{ .compatible = "qcom,sdm845-cdsp-pas", .data = &sdm845_cdsp_resource_init},
|
||||
|
|
|
|||
|
|
@ -32,21 +32,10 @@ static ssize_t rproc_cdev_write(struct file *filp, const char __user *buf, size_
|
|||
return -EFAULT;
|
||||
|
||||
if (!strncmp(cmd, "start", len)) {
|
||||
if (rproc->state == RPROC_RUNNING ||
|
||||
rproc->state == RPROC_ATTACHED)
|
||||
return -EBUSY;
|
||||
|
||||
ret = rproc_boot(rproc);
|
||||
} else if (!strncmp(cmd, "stop", len)) {
|
||||
if (rproc->state != RPROC_RUNNING &&
|
||||
rproc->state != RPROC_ATTACHED)
|
||||
return -EINVAL;
|
||||
|
||||
ret = rproc_shutdown(rproc);
|
||||
} else if (!strncmp(cmd, "detach", len)) {
|
||||
if (rproc->state != RPROC_ATTACHED)
|
||||
return -EINVAL;
|
||||
|
||||
ret = rproc_detach(rproc);
|
||||
} else {
|
||||
dev_err(&rproc->dev, "Unrecognized option\n");
|
||||
|
|
|
|||
|
|
@ -684,10 +684,6 @@ static int rproc_handle_trace(struct rproc *rproc, void *ptr,
|
|||
|
||||
/* create the debugfs entry */
|
||||
trace->tfile = rproc_create_trace_file(name, rproc, trace);
|
||||
if (!trace->tfile) {
|
||||
kfree(trace);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
list_add_tail(&trace->node, &rproc->traces);
|
||||
|
||||
|
|
@ -2075,6 +2071,12 @@ int rproc_shutdown(struct rproc *rproc)
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (rproc->state != RPROC_RUNNING &&
|
||||
rproc->state != RPROC_ATTACHED) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* if the remote proc is still needed, bail out */
|
||||
if (!atomic_dec_and_test(&rproc->power))
|
||||
goto out;
|
||||
|
|
@ -2134,6 +2136,11 @@ int rproc_detach(struct rproc *rproc)
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (rproc->state != RPROC_ATTACHED) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* if the remote proc is still needed, bail out */
|
||||
if (!atomic_dec_and_test(&rproc->power)) {
|
||||
ret = 0;
|
||||
|
|
|
|||
|
|
@ -386,16 +386,8 @@ void rproc_remove_trace_file(struct dentry *tfile)
|
|||
struct dentry *rproc_create_trace_file(const char *name, struct rproc *rproc,
|
||||
struct rproc_debug_trace *trace)
|
||||
{
|
||||
struct dentry *tfile;
|
||||
|
||||
tfile = debugfs_create_file(name, 0400, rproc->dbg_dir, trace,
|
||||
return debugfs_create_file(name, 0400, rproc->dbg_dir, trace,
|
||||
&trace_rproc_ops);
|
||||
if (!tfile) {
|
||||
dev_err(&rproc->dev, "failed to create debugfs trace entry\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return tfile;
|
||||
}
|
||||
|
||||
void rproc_delete_debug_dir(struct rproc *rproc)
|
||||
|
|
@ -411,8 +403,6 @@ void rproc_create_debug_dir(struct rproc *rproc)
|
|||
return;
|
||||
|
||||
rproc->dbg_dir = debugfs_create_dir(dev_name(dev), rproc_dbg);
|
||||
if (!rproc->dbg_dir)
|
||||
return;
|
||||
|
||||
debugfs_create_file("name", 0400, rproc->dbg_dir,
|
||||
rproc, &rproc_name_ops);
|
||||
|
|
@ -430,11 +420,8 @@ void rproc_create_debug_dir(struct rproc *rproc)
|
|||
|
||||
void __init rproc_init_debugfs(void)
|
||||
{
|
||||
if (debugfs_initialized()) {
|
||||
if (debugfs_initialized())
|
||||
rproc_dbg = debugfs_create_dir(KBUILD_MODNAME, NULL);
|
||||
if (!rproc_dbg)
|
||||
pr_err("can't create debugfs dir\n");
|
||||
}
|
||||
}
|
||||
|
||||
void __exit rproc_exit_debugfs(void)
|
||||
|
|
|
|||
|
|
@ -181,7 +181,7 @@ int rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw)
|
|||
bool is_iomem = false;
|
||||
void *ptr;
|
||||
|
||||
if (type != PT_LOAD)
|
||||
if (type != PT_LOAD || !memsz)
|
||||
continue;
|
||||
|
||||
dev_dbg(dev, "phdr: type %d da 0x%llx memsz 0x%llx filesz 0x%llx\n",
|
||||
|
|
|
|||
|
|
@ -194,23 +194,12 @@ static ssize_t state_store(struct device *dev,
|
|||
int ret = 0;
|
||||
|
||||
if (sysfs_streq(buf, "start")) {
|
||||
if (rproc->state == RPROC_RUNNING ||
|
||||
rproc->state == RPROC_ATTACHED)
|
||||
return -EBUSY;
|
||||
|
||||
ret = rproc_boot(rproc);
|
||||
if (ret)
|
||||
dev_err(&rproc->dev, "Boot failed: %d\n", ret);
|
||||
} else if (sysfs_streq(buf, "stop")) {
|
||||
if (rproc->state != RPROC_RUNNING &&
|
||||
rproc->state != RPROC_ATTACHED)
|
||||
return -EINVAL;
|
||||
|
||||
ret = rproc_shutdown(rproc);
|
||||
} else if (sysfs_streq(buf, "detach")) {
|
||||
if (rproc->state != RPROC_ATTACHED)
|
||||
return -EINVAL;
|
||||
|
||||
ret = rproc_detach(rproc);
|
||||
} else {
|
||||
dev_err(&rproc->dev, "Unrecognised option: %s\n", buf);
|
||||
|
|
|
|||
|
|
@ -1407,9 +1407,9 @@ static int qcom_smd_parse_edge(struct device *dev,
|
|||
edge->name = node->name;
|
||||
|
||||
irq = irq_of_parse_and_map(node, 0);
|
||||
if (irq < 0) {
|
||||
if (!irq) {
|
||||
dev_err(dev, "required smd interrupt missing\n");
|
||||
ret = irq;
|
||||
ret = -EINVAL;
|
||||
goto put_node;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -851,7 +851,7 @@ static struct rpmsg_device *rpmsg_virtio_add_ctrl_dev(struct virtio_device *vdev
|
|||
|
||||
err = rpmsg_ctrldev_register_device(rpdev_ctrl);
|
||||
if (err) {
|
||||
kfree(vch);
|
||||
/* vch will be free in virtio_rpmsg_release_device() */
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
|
|
@ -862,7 +862,7 @@ static void rpmsg_virtio_del_ctrl_dev(struct rpmsg_device *rpdev_ctrl)
|
|||
{
|
||||
if (!rpdev_ctrl)
|
||||
return;
|
||||
kfree(to_virtio_rpmsg_channel(rpdev_ctrl));
|
||||
device_unregister(&rpdev_ctrl->dev);
|
||||
}
|
||||
|
||||
static int rpmsg_probe(struct virtio_device *vdev)
|
||||
|
|
@ -973,7 +973,8 @@ static int rpmsg_probe(struct virtio_device *vdev)
|
|||
|
||||
err = rpmsg_ns_register_device(rpdev_ns);
|
||||
if (err)
|
||||
goto free_vch;
|
||||
/* vch will be free in virtio_rpmsg_release_device() */
|
||||
goto free_ctrldev;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -997,8 +998,6 @@ static int rpmsg_probe(struct virtio_device *vdev)
|
|||
|
||||
return 0;
|
||||
|
||||
free_vch:
|
||||
kfree(vch);
|
||||
free_ctrldev:
|
||||
rpmsg_virtio_del_ctrl_dev(rpdev_ctrl);
|
||||
free_coherent:
|
||||
|
|
|
|||
|
|
@ -1548,6 +1548,13 @@ config RTC_DRV_RS5C313
|
|||
help
|
||||
If you say yes here you get support for the Ricoh RS5C313 RTC chips.
|
||||
|
||||
config RTC_DRV_RZN1
|
||||
tristate "Renesas RZ/N1 RTC"
|
||||
depends on ARCH_RZN1 || COMPILE_TEST
|
||||
depends on OF && HAS_IOMEM
|
||||
help
|
||||
If you say yes here you get support for the Renesas RZ/N1 RTC.
|
||||
|
||||
config RTC_DRV_GENERIC
|
||||
tristate "Generic RTC support"
|
||||
# Please consider writing a new RTC driver instead of using the generic
|
||||
|
|
|
|||
|
|
@ -151,6 +151,7 @@ obj-$(CONFIG_RTC_DRV_RX6110) += rtc-rx6110.o
|
|||
obj-$(CONFIG_RTC_DRV_RX8010) += rtc-rx8010.o
|
||||
obj-$(CONFIG_RTC_DRV_RX8025) += rtc-rx8025.o
|
||||
obj-$(CONFIG_RTC_DRV_RX8581) += rtc-rx8581.o
|
||||
obj-$(CONFIG_RTC_DRV_RZN1) += rtc-rzn1.o
|
||||
obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o
|
||||
obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o
|
||||
obj-$(CONFIG_RTC_DRV_S5M) += rtc-s5m.o
|
||||
|
|
|
|||
|
|
@ -137,26 +137,34 @@ static int ftrtc010_rtc_probe(struct platform_device *pdev)
|
|||
ret = clk_prepare_enable(rtc->extclk);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable EXTCLK\n");
|
||||
return ret;
|
||||
goto err_disable_pclk;
|
||||
}
|
||||
}
|
||||
|
||||
rtc->rtc_irq = platform_get_irq(pdev, 0);
|
||||
if (rtc->rtc_irq < 0)
|
||||
return rtc->rtc_irq;
|
||||
if (rtc->rtc_irq < 0) {
|
||||
ret = rtc->rtc_irq;
|
||||
goto err_disable_extclk;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -ENODEV;
|
||||
if (!res) {
|
||||
ret = -ENODEV;
|
||||
goto err_disable_extclk;
|
||||
}
|
||||
|
||||
rtc->rtc_base = devm_ioremap(dev, res->start,
|
||||
resource_size(res));
|
||||
if (!rtc->rtc_base)
|
||||
return -ENOMEM;
|
||||
if (!rtc->rtc_base) {
|
||||
ret = -ENOMEM;
|
||||
goto err_disable_extclk;
|
||||
}
|
||||
|
||||
rtc->rtc_dev = devm_rtc_allocate_device(dev);
|
||||
if (IS_ERR(rtc->rtc_dev))
|
||||
return PTR_ERR(rtc->rtc_dev);
|
||||
if (IS_ERR(rtc->rtc_dev)) {
|
||||
ret = PTR_ERR(rtc->rtc_dev);
|
||||
goto err_disable_extclk;
|
||||
}
|
||||
|
||||
rtc->rtc_dev->ops = &ftrtc010_rtc_ops;
|
||||
|
||||
|
|
@ -172,9 +180,15 @@ static int ftrtc010_rtc_probe(struct platform_device *pdev)
|
|||
ret = devm_request_irq(dev, rtc->rtc_irq, ftrtc010_rtc_interrupt,
|
||||
IRQF_SHARED, pdev->name, dev);
|
||||
if (unlikely(ret))
|
||||
return ret;
|
||||
goto err_disable_extclk;
|
||||
|
||||
return devm_rtc_register_device(rtc->rtc_dev);
|
||||
|
||||
err_disable_extclk:
|
||||
clk_disable_unprepare(rtc->extclk);
|
||||
err_disable_pclk:
|
||||
clk_disable_unprepare(rtc->pclk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ftrtc010_rtc_remove(struct platform_device *pdev)
|
||||
|
|
|
|||
|
|
@ -267,6 +267,7 @@ static int gamecube_rtc_read_offset_from_sram(struct priv *d)
|
|||
ret = regmap_read(d->regmap, RTC_SRAM_BIAS, &d->rtc_bias);
|
||||
if (ret) {
|
||||
pr_err("failed to get the RTC bias\n");
|
||||
iounmap(hw_srnprot);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -399,7 +399,7 @@ static struct platform_driver meson_rtc_driver = {
|
|||
module_platform_driver(meson_rtc_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Amlogic Meson RTC Driver");
|
||||
MODULE_AUTHOR("Ben Dooks <ben.doosk@codethink.co.uk>");
|
||||
MODULE_AUTHOR("Ben Dooks <ben.dooks@codethink.co.uk>");
|
||||
MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:meson-rtc");
|
||||
|
|
|
|||
|
|
@ -269,6 +269,8 @@ static int mtk_rtc_probe(struct platform_device *pdev)
|
|||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -EINVAL;
|
||||
rtc->addr_base = res->start;
|
||||
|
||||
rtc->data = of_device_get_match_data(&pdev->dev);
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue