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:
Greg Kroah-Hartman 2022-06-29 19:57:47 +02:00
commit 2f60073848
243 changed files with 7855 additions and 4489 deletions

View file

@ -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>

View file

@ -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

View file

@ -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>;
};
};

View 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>;
};
};
...

View file

@ -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>;
};
};

View file

@ -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>;
};
};
...

View file

@ -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:

View 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>;
};

View file

@ -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>;
};
};

View file

@ -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

View 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";
};

View file

@ -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.

View file

@ -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>;
};

View file

@ -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>;
};

View file

@ -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

View file

@ -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>;

View file

@ -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

View file

@ -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

View file

@ -2,6 +2,7 @@
Required properties:
- compatible: Should one of contain:
"nxp,pca85073a",
"nxp,pcf85063",
"nxp,pcf85063a",
"nxp,pcf85063tp",

View 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>;
};

View 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>;
};

View file

@ -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

View file

@ -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>;
};

View file

@ -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";
};
...

View file

@ -19,6 +19,7 @@ properties:
- items:
- const: fsl,imx8ulp-wdt
- const: fsl,imx7ulp-wdt
- const: fsl,imx93-wdt
reg:
maxItems: 1

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -19,7 +19,7 @@ properties:
required:
- compatible
additionalProperties: false
unevaluatedProperties: false
examples:
- |

View file

@ -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>;
};
...

View file

@ -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

View file

@ -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

View file

@ -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

View 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.

View file

@ -6,6 +6,8 @@ NFS
.. toctree::
:maxdepth: 1
client-identifier
exporting
pnfs
rpc-cache
rpc-server-gss

View file

@ -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

View file

@ -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;

View file

@ -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)
{

View file

@ -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>

View file

@ -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>

View file

@ -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

View file

@ -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 */

View file

@ -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;
}
/*

View file

@ -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));

View file

@ -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);

View file

@ -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;

View file

@ -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;

View file

@ -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[] = {

View file

@ -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 = {

View file

@ -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;
}
/*

View file

@ -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,

View file

@ -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);

View file

@ -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 = {

View file

@ -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.

View file

@ -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

View file

@ -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;

View file

@ -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,

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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;

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -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,
};

View file

@ -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, &params->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, &params->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);

View file

@ -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,
};

View file

@ -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,

View file

@ -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,
};

View file

@ -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 },

View file

@ -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;

View file

@ -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),

View file

@ -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,
};

View file

@ -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;
}

View file

@ -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,
};

View file

@ -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,
};

View file

@ -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
View 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");

View file

@ -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,
};

View file

@ -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
View 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");

View file

@ -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,
};

View file

@ -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);

View file

@ -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

View file

@ -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,

View file

@ -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},

View file

@ -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");

View file

@ -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;

View file

@ -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)

View file

@ -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",

View file

@ -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);

View file

@ -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;
}

View file

@ -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:

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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;
}

View file

@ -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");

View file

@ -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