Commit e85195d5 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'drivers-5.17' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc

Pull ARM SoC driver updates from Arnd Bergmann:
 "There are cleanups and minor bugfixes across several SoC specific
  drivers, for Qualcomm, Samsung, NXP i.MX, AT91, Tegra, Keystone,
  Renesas, ZynqMP

  Noteworthy new features are:

   - The op-tee firmware driver gains support for asynchronous
     notifications from secure-world firmware.

   - Qualcomm platforms gain support for new SoC types in various
     drivers: power domain, cache controller, RPM sleep, soc-info

   - Samsung SoC drivers gain support for new SoCs in ChipID and PMU, as
     well as a new USIv2 driver that handles various types of serial
     communiction (uart, i2c, spi)

   - Renesas adds support for R-Car S4-8 (R8A779F0) in multiple drivers,
     as well as memory controller support for RZ/G2L (R9A07G044).

   - Apple M1 gains support for the PMGR power management driver"

* tag 'drivers-5.17' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc: (94 commits)
  soc: qcom: rpmh-rsc: Fix typo in a comment
  soc: qcom: socinfo: Add SM6350 and SM7225
  dt-bindings: arm: msm: Don't mark LLCC interrupt as required
  dt-bindings: firmware: scm: Add SM6350 compatible
  dt-bindings: arm: msm: Add LLCC for SM6350
  soc: qcom: rpmhpd: Sort power-domain definitions and lists
  soc: qcom: rpmhpd: Remove mx/cx relationship on sc7280
  soc: qcom: rpmhpd: Rename rpmhpd struct names
  soc: qcom: rpmhpd: sm8450: Add the missing .peer for sm8450_cx_ao
  soc: qcom: socinfo: add SM8450 ID
  soc: qcom: rpmhpd: Add SM8450 power domains
  dt-bindings: power: rpmpd: Add SM8450 to rpmpd binding
  soc: qcom: smem: Update max processor count
  dt-bindings: arm: qcom: Document SM8450 SoC and boards
  dt-bindings: firmware: scm: Add SM8450 compatible
  dt-bindings: arm: cpus: Add kryo780 compatible
  soc: qcom: rpmpd: Add support for sm6125
  dt-bindings: qcom-rpmpd: Add sm6125 power domains
  soc: qcom: aoss: constify static struct thermal_cooling_device_ops
  PM: AVS: qcom-cpr: Use div64_ul instead of do_div
  ...
parents 0dca3c5e 13ee75c7
......@@ -174,6 +174,7 @@ properties:
- qcom,kryo560
- qcom,kryo570
- qcom,kryo685
- qcom,kryo780
- qcom,scorpion
enable-method:
......
......@@ -24,6 +24,12 @@ properties:
compatible:
const: linaro,optee-tz
interrupts:
maxItems: 1
description: |
This interrupt which is used to signal an event by the secure world
software is expected to be edge-triggered.
method:
enum: [smc, hvc]
description: |
......@@ -42,10 +48,12 @@ additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
firmware {
optee {
compatible = "linaro,optee-tz";
method = "smc";
interrupts = <GIC_SPI 187 IRQ_TYPE_EDGE_RISING>;
};
};
......
......@@ -24,6 +24,7 @@ properties:
- qcom,sc7180-llcc
- qcom,sc7280-llcc
- qcom,sdm845-llcc
- qcom,sm6350-llcc
- qcom,sm8150-llcc
- qcom,sm8250-llcc
......@@ -44,7 +45,6 @@ required:
- compatible
- reg
- reg-names
- interrupts
additionalProperties: false
......
......@@ -50,6 +50,7 @@ description: |
sm8150
sm8250
sm8350
sm8450
The 'board' element must be one of the following strings:
......@@ -257,6 +258,11 @@ properties:
- qcom,sm8350-mtp
- const: qcom,sm8350
- items:
- enum:
- qcom,sm8450-qrd
- const: qcom,sm8450
additionalProperties: true
...
......@@ -26,9 +26,11 @@ Required properties:
* "qcom,scm-sc7280"
* "qcom,scm-sdm845"
* "qcom,scm-sdx55"
* "qcom,scm-sm6350"
* "qcom,scm-sm8150"
* "qcom,scm-sm8250"
* "qcom,scm-sm8350"
* "qcom,scm-sm8450"
and:
* "qcom,scm"
- clocks: Specifies clocks needed by the SCM interface, if any:
......
......@@ -24,17 +24,23 @@ allOf:
properties:
compatible:
items:
- enum:
- renesas,r8a774a1-rpc-if # RZ/G2M
- renesas,r8a774b1-rpc-if # RZ/G2N
- renesas,r8a774c0-rpc-if # RZ/G2E
- renesas,r8a774e1-rpc-if # RZ/G2H
- renesas,r8a77970-rpc-if # R-Car V3M
- renesas,r8a77980-rpc-if # R-Car V3H
- renesas,r8a77995-rpc-if # R-Car D3
- renesas,r8a779a0-rpc-if # R-Car V3U
- const: renesas,rcar-gen3-rpc-if # a generic R-Car gen3 or RZ/G2 device
oneOf:
- items:
- enum:
- renesas,r8a774a1-rpc-if # RZ/G2M
- renesas,r8a774b1-rpc-if # RZ/G2N
- renesas,r8a774c0-rpc-if # RZ/G2E
- renesas,r8a774e1-rpc-if # RZ/G2H
- renesas,r8a77970-rpc-if # R-Car V3M
- renesas,r8a77980-rpc-if # R-Car V3H
- renesas,r8a77995-rpc-if # R-Car D3
- renesas,r8a779a0-rpc-if # R-Car V3U
- const: renesas,rcar-gen3-rpc-if # a generic R-Car gen3 or RZ/G2{E,H,M,N} device
- items:
- enum:
- renesas,r9a07g044-rpc-if # RZ/G2{L,LC}
- const: renesas,rzg2l-rpc-if # RZ/G2L family
reg:
items:
......@@ -48,7 +54,9 @@ properties:
- const: dirmap
- const: wbuf
clocks:
clocks: true
interrupts:
maxItems: 1
power-domains:
......@@ -67,8 +75,6 @@ patternProperties:
- cfi-flash
- jedec,spi-nor
unevaluatedProperties: false
required:
- compatible
- reg
......@@ -79,6 +85,26 @@ required:
- '#address-cells'
- '#size-cells'
if:
properties:
compatible:
contains:
enum:
- renesas,rzg2l-rpc-if
then:
properties:
clocks:
items:
- description: SPI Multi IO Register access clock (SPI_CLK2)
- description: SPI Multi IO Main clock (SPI_CLK).
else:
properties:
clocks:
maxItems: 1
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/clock/renesas-cpg-mssr.h>
......
......@@ -24,6 +24,7 @@ properties:
- qcom,msm8994-rpmpd
- qcom,msm8996-rpmpd
- qcom,msm8998-rpmpd
- qcom,qcm2290-rpmpd
- qcom,qcs404-rpmpd
- qcom,sdm660-rpmpd
- qcom,sc7180-rpmhpd
......@@ -32,10 +33,12 @@ properties:
- qcom,sdm845-rpmhpd
- qcom,sdx55-rpmhpd
- qcom,sm6115-rpmpd
- qcom,sm6125-rpmpd
- qcom,sm6350-rpmhpd
- qcom,sm8150-rpmhpd
- qcom,sm8250-rpmhpd
- qcom,sm8350-rpmhpd
- qcom,sm8450-rpmhpd
'#power-domain-cells':
const: 1
......
......@@ -21,6 +21,11 @@ properties:
enum:
- qcom,rpmh-stats
- qcom,rpm-stats
# For older RPM firmware versions with fixed offset for the sleep stats
- qcom,apq8084-rpm-stats
- qcom,msm8226-rpm-stats
- qcom,msm8916-rpm-stats
- qcom,msm8974-rpm-stats
reg:
maxItems: 1
......
# SPDX-License-Identifier: GPL-2.0
%YAML 1.2
---
$id: http://devicetree.org/schemas/arm/samsung/exynos-chipid.yaml#
$id: http://devicetree.org/schemas/soc/samsung/exynos-chipid.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Samsung Exynos SoC series Chipid driver
......
# SPDX-License-Identifier: GPL-2.0
%YAML 1.2
---
$id: http://devicetree.org/schemas/arm/samsung/pmu.yaml#
$id: http://devicetree.org/schemas/soc/samsung/exynos-pmu.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Samsung Exynos SoC series Power Management Unit (PMU)
......@@ -24,6 +24,7 @@ select:
- samsung,exynos5420-pmu
- samsung,exynos5433-pmu
- samsung,exynos7-pmu
- samsung,exynos850-pmu
- samsung-s5pv210-pmu
required:
- compatible
......@@ -41,6 +42,7 @@ properties:
- samsung,exynos5420-pmu
- samsung,exynos5433-pmu
- samsung,exynos7-pmu
- samsung,exynos850-pmu
- samsung-s5pv210-pmu
- const: syscon
......
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/soc/samsung/exynos-usi.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Samsung's Exynos USI (Universal Serial Interface) binding
maintainers:
- Sam Protsenko <semen.protsenko@linaro.org>
- Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com>
description: |
USI IP-core provides selectable serial protocol (UART, SPI or High-Speed I2C).
USI shares almost all internal circuits within each protocol, so only one
protocol can be chosen at a time. USI is modeled as a node with zero or more
child nodes, each representing a serial sub-node device. The mode setting
selects which particular function will be used.
Refer to next bindings documentation for information on protocol subnodes that
can exist under USI node:
[1] Documentation/devicetree/bindings/serial/samsung_uart.yaml
[2] Documentation/devicetree/bindings/i2c/i2c-exynos5.txt
[3] Documentation/devicetree/bindings/spi/spi-samsung.txt
properties:
$nodename:
pattern: "^usi@[0-9a-f]+$"
compatible:
enum:
- samsung,exynos850-usi # for USIv2 (Exynos850, ExynosAutoV9)
reg: true
clocks: true
clock-names: true
ranges: true
"#address-cells":
const: 1
"#size-cells":
const: 1
samsung,sysreg:
$ref: /schemas/types.yaml#/definitions/phandle-array
description:
Should be phandle/offset pair. The phandle to System Register syscon node
(for the same domain where this USI controller resides) and the offset
of SW_CONF register for this USI controller.
samsung,mode:
$ref: /schemas/types.yaml#/definitions/uint32
description:
Selects USI function (which serial protocol to use). Refer to
<include/dt-bindings/soc/samsung,exynos-usi.h> for valid USI mode values.
samsung,clkreq-on:
type: boolean
description:
Enable this property if underlying protocol requires the clock to be
continuously provided without automatic gating. As suggested by SoC
manual, it should be set in case of SPI/I2C slave, UART Rx and I2C
multi-master mode. Usually this property is needed if USI mode is set
to "UART".
This property is optional.
patternProperties:
# All other properties should be child nodes
"^(serial|spi|i2c)@[0-9a-f]+$":
type: object
description: Child node describing underlying USI serial protocol
required:
- compatible
- ranges
- "#address-cells"
- "#size-cells"
- samsung,sysreg
- samsung,mode
if:
properties:
compatible:
contains:
enum:
- samsung,exynos850-usi
then:
properties:
reg:
maxItems: 1
clocks:
items:
- description: Bus (APB) clock
- description: Operating clock for UART/SPI/I2C protocol
clock-names:
items:
- const: pclk
- const: ipclk
required:
- reg
- clocks
- clock-names
else:
properties:
reg: false
clocks: false
clock-names: false
samsung,clkreq-on: false
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/soc/samsung,exynos-usi.h>
usi0: usi@138200c0 {
compatible = "samsung,exynos850-usi";
reg = <0x138200c0 0x20>;
samsung,sysreg = <&sysreg_peri 0x1010>;
samsung,mode = <USI_V2_UART>;
samsung,clkreq-on; /* needed for UART mode */
#address-cells = <1>;
#size-cells = <1>;
ranges;
clocks = <&cmu_peri 32>, <&cmu_peri 31>;
clock-names = "pclk", "ipclk";
serial_0: serial@13820000 {
compatible = "samsung,exynos850-uart";
reg = <0x13820000 0xc0>;
interrupts = <GIC_SPI 227 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cmu_peri 32>, <&cmu_peri 31>;
clock-names = "uart", "clk_uart_baud0";
status = "disabled";
};
hsi2c_0: i2c@13820000 {
compatible = "samsung,exynosautov9-hsi2c";
reg = <0x13820000 0xc0>;
interrupts = <GIC_SPI 227 IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <1>;
#size-cells = <0>;
clocks = <&cmu_peri 32>, <&cmu_peri 31>;
clock-names = "hsi2c_pclk", "hsi2c";
status = "disabled";
};
};
......@@ -184,6 +184,36 @@ order to support device enumeration. In other words, OP-TEE driver invokes this
application to retrieve a list of Trusted Applications which can be registered
as devices on the TEE bus.
OP-TEE notifications
--------------------
There are two kinds of notifications that secure world can use to make
normal world aware of some event.
1. Synchronous notifications delivered with ``OPTEE_RPC_CMD_NOTIFICATION``
using the ``OPTEE_RPC_NOTIFICATION_SEND`` parameter.
2. Asynchronous notifications delivered with a combination of a non-secure
edge-triggered interrupt and a fast call from the non-secure interrupt
handler.
Synchronous notifications are limited by depending on RPC for delivery,
this is only usable when secure world is entered with a yielding call via
``OPTEE_SMC_CALL_WITH_ARG``. This excludes such notifications from secure
world interrupt handlers.
An asynchronous notification is delivered via a non-secure edge-triggered
interrupt to an interrupt handler registered in the OP-TEE driver. The
actual notification value are retrieved with the fast call
``OPTEE_SMC_GET_ASYNC_NOTIF_VALUE``. Note that one interrupt can represent
multiple notifications.
One notification value ``OPTEE_SMC_ASYNC_NOTIF_VALUE_DO_BOTTOM_HALF`` has a
special meaning. When this value is received it means that normal world is
supposed to make a yielding call ``OPTEE_MSG_CMD_DO_BOTTOM_HALF``. This
call is done from the thread assisting the interrupt handler. This is a
building block for OP-TEE OS in secure world to implement the top half and
bottom half style of device drivers.
AMD-TEE driver
==============
......
......@@ -2551,6 +2551,7 @@ Q: https://patchwork.kernel.org/project/linux-samsung-soc/list/
F: Documentation/arm/samsung/
F: Documentation/devicetree/bindings/arm/samsung/
F: Documentation/devicetree/bindings/power/pd-samsung.yaml
F: Documentation/devicetree/bindings/soc/samsung/
F: arch/arm/boot/dts/exynos*
F: arch/arm/boot/dts/s3c*
F: arch/arm/boot/dts/s5p*
......
......@@ -645,6 +645,7 @@ static __init int at91_dt_ramc(bool phy_mandatory)
if (!soc_pm.data.ramc[idx]) {
pr_err("unable to map ramc[%d] cpu registers\n", idx);
ret = -ENOMEM;
of_node_put(np);
goto unmap_ramc;
}
......@@ -670,6 +671,7 @@ static __init int at91_dt_ramc(bool phy_mandatory)
if (!soc_pm.data.ramc_phy) {
pr_err("unable to map ramc phy cpu registers\n");
ret = -ENOMEM;
of_node_put(np);
goto unmap_ramc;
}
}
......
......@@ -21,6 +21,7 @@ struct imx_weim_devtype {
unsigned int cs_stride;
unsigned int wcr_offset;
unsigned int wcr_bcm;
unsigned int wcr_cont_bclk;
};
static const struct imx_weim_devtype imx1_weim_devtype = {
......@@ -41,6 +42,7 @@ static const struct imx_weim_devtype imx50_weim_devtype = {
.cs_stride = 0x18,
.wcr_offset = 0x90,
.wcr_bcm = BIT(0),
.wcr_cont_bclk = BIT(3),
};
static const struct imx_weim_devtype imx51_weim_devtype = {
......@@ -206,8 +208,20 @@ static int weim_parse_dt(struct platform_device *pdev, void __iomem *base)
if (of_property_read_bool(pdev->dev.of_node, "fsl,burst-clk-enable")) {
if (devtype->wcr_bcm) {
reg = readl(base + devtype->wcr_offset);
writel(reg | devtype->wcr_bcm,
base + devtype->wcr_offset);
reg |= devtype->wcr_bcm;
if (of_property_read_bool(pdev->dev.of_node,
"fsl,continuous-burst-clk")) {
if (devtype->wcr_cont_bclk) {
reg |= devtype->wcr_cont_bclk;
} else {
dev_err(&pdev->dev,
"continuous burst clk not supported.\n");
return -EINVAL;
}
}
writel(reg, base + devtype->wcr_offset);
} else {
dev_err(&pdev->dev, "burst clk mode not supported.\n");
return -EINVAL;
......
......@@ -13,8 +13,11 @@
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <soc/tegra/common.h>
#define TEGRA_GMI_CONFIG 0x00
#define TEGRA_GMI_CONFIG_GO BIT(31)
#define TEGRA_GMI_BUS_WIDTH_32BIT BIT(30)
......@@ -54,9 +57,10 @@ static int tegra_gmi_enable(struct tegra_gmi *gmi)
{
int err;
err = clk_prepare_enable(gmi->clk);
if (err < 0) {
dev_err(gmi->dev, "failed to enable clock: %d\n", err);
pm_runtime_enable(gmi->dev);
err = pm_runtime_resume_and_get(gmi->dev);
if (err) {
pm_runtime_disable(gmi->dev);
return err;
}
......@@ -83,7 +87,9 @@ static void tegra_gmi_disable(struct tegra_gmi *gmi)
writel(config, gmi->base + TEGRA_GMI_CONFIG);
reset_control_assert(gmi->rst);
clk_disable_unprepare(gmi->clk);
pm_runtime_put_sync_suspend(gmi->dev);
pm_runtime_force_suspend(gmi->dev);
}
static int tegra_gmi_parse_dt(struct tegra_gmi *gmi)
......@@ -213,6 +219,7 @@ static int tegra_gmi_probe(struct platform_device *pdev)
if (!gmi)
return -ENOMEM;
platform_set_drvdata(pdev, gmi);
gmi->dev = dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
......@@ -232,6 +239,10 @@ static int tegra_gmi_probe(struct platform_device *pdev)
return PTR_ERR(gmi->rst);
}
err = devm_tegra_core_dev_init_opp_table_common(&pdev->dev);
if (err)
return err;
err = tegra_gmi_parse_dt(gmi);
if (err)
return err;
......@@ -247,8 +258,6 @@ static int tegra_gmi_probe(struct platform_device *pdev)
return err;
}
platform_set_drvdata(pdev, gmi);
return 0;
}
......@@ -262,6 +271,34 @@ static int tegra_gmi_remove(struct platform_device *pdev)
return 0;
}
static int __maybe_unused tegra_gmi_runtime_resume(struct device *dev)
{
struct tegra_gmi *gmi = dev_get_drvdata(dev);
int err;
err = clk_prepare_enable(gmi->clk);
if (err < 0) {
dev_err(gmi->dev, "failed to enable clock: %d\n", err);
return err;
}
return 0;
}
static int __maybe_unused tegra_gmi_runtime_suspend(struct device *dev)
{
struct tegra_gmi *gmi = dev_get_drvdata(dev);
clk_disable_unprepare(gmi->clk);
return 0;
}
static const struct dev_pm_ops tegra_gmi_pm = {
SET_RUNTIME_PM_OPS(tegra_gmi_runtime_suspend, tegra_gmi_runtime_resume,
NULL)
};
static const struct of_device_id tegra_gmi_id_table[] = {
{ .compatible = "nvidia,tegra20-gmi", },
{ .compatible = "nvidia,tegra30-gmi", },
......@@ -275,6 +312,7 @@ static struct platform_driver tegra_gmi_driver = {
.driver = {
.name = "tegra-gmi",
.of_match_table = tegra_gmi_id_table,
.pm = &tegra_gmi_pm,
},
};
module_platform_driver(tegra_gmi_driver);
......
......@@ -1759,7 +1759,7 @@ static int ti_sci_get_resource_range(const struct ti_sci_handle *handle,
desc->num = resp->range_num;
desc->start_sec = resp->range_start_sec;
desc->num_sec = resp->range_num_sec;
};
}
fail:
ti_sci_put_one_xfer(&info->minfo, xfer);
......
......@@ -1434,7 +1434,10 @@ static int zynqmp_firmware_probe(struct platform_device *pdev)
return ret;
/* Check PM API version number */
zynqmp_pm_get_api_version(&pm_api_version);
ret = zynqmp_pm_get_api_version(&pm_api_version);
if (ret)
return ret;
if (pm_api_version < ZYNQMP_PM_VERSION) {
panic("%s Platform Management API version error. Expected: v%d.%d - Found: v%d.%d\n",
__func__,
......
......@@ -12,6 +12,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/regmap.h>
#include <linux/reset.h>
......@@ -19,19 +20,17 @@
#define RPCIF_CMNCR 0x0000 /* R/W */
#define RPCIF_CMNCR_MD BIT(31)
#define RPCIF_CMNCR_SFDE BIT(24) /* undocumented but must be set */
#define RPCIF_CMNCR_MOIIO3(val) (((val) & 0x3) << 22)
#define RPCIF_CMNCR_MOIIO2(val) (((val) & 0x3) << 20)
#define RPCIF_CMNCR_MOIIO1(val) (((val) & 0x3) << 18)
#define RPCIF_CMNCR_MOIIO0(val) (((val) & 0x3) << 16)
#define RPCIF_CMNCR_MOIIO_HIZ (RPCIF_CMNCR_MOIIO0(3) | \
RPCIF_CMNCR_MOIIO1(3) | \
RPCIF_CMNCR_MOIIO2(3) | RPCIF_CMNCR_MOIIO3(3))
#define RPCIF_CMNCR_IO3FV(val) (((val) & 0x3) << 14) /* undocumented */
#define RPCIF_CMNCR_IO2FV(val) (((val) & 0x3) << 12) /* undocumented */
#define RPCIF_CMNCR_MOIIO(val) (RPCIF_CMNCR_MOIIO0(val) | RPCIF_CMNCR_MOIIO1(val) | \
RPCIF_CMNCR_MOIIO2(val) | RPCIF_CMNCR_MOIIO3(val))
#define RPCIF_CMNCR_IO3FV(val) (((val) & 0x3) << 14) /* documented for RZ/G2L */
#define RPCIF_CMNCR_IO2FV(val) (((val) & 0x3) << 12) /* documented for RZ/G2L */
#define RPCIF_CMNCR_IO0FV(val) (((val) & 0x3) << 8)
#define RPCIF_CMNCR_IOFV_HIZ (RPCIF_CMNCR_IO0FV(3) | RPCIF_CMNCR_IO2FV(3) | \
RPCIF_CMNCR_IO3FV(3))
#define RPCIF_CMNCR_IOFV(val) (RPCIF_CMNCR_IO0FV(val) | RPCIF_CMNCR_IO2FV(val) | \
RPCIF_CMNCR_IO3FV(val))
#define RPCIF_CMNCR_BSZ(val) (((val) & 0x3) << 0)
#define RPCIF_SSLDR 0x0004 /* R/W */
......@@ -126,6 +125,9 @@
#define RPCIF_SMDRENR_OPDRE BIT(4)
#define RPCIF_SMDRENR_SPIDRE BIT(0)
#define RPCIF_PHYADD 0x0070 /* R/W available on R-Car E3/D3/V3M and RZ/G2{E,L} */
#define RPCIF_PHYWR 0x0074 /* R/W available on R-Car E3/D3/V3M and RZ/G2{E,L} */
#define RPCIF_PHYCNT 0x007C /* R/W */
#define RPCIF_PHYCNT_CAL BIT(31)
#define RPCIF_PHYCNT_OCTA(v) (((v) & 0x3) << 22)
......@@ -133,10 +135,12 @@
#define RPCIF_PHYCNT_OCT BIT(20)
#define RPCIF_PHYCNT_DDRCAL BIT(19)
#define RPCIF_PHYCNT_HS BIT(18)
#define RPCIF_PHYCNT_STRTIM(v) (((v) & 0x7) << 15)
#define RPCIF_PHYCNT_CKSEL(v) (((v) & 0x3) << 16) /* valid only for RZ/G2L */
#define RPCIF_PHYCNT_STRTIM(v) (((v) & 0x7) << 15) /* valid for R-Car and RZ/G2{E,H,M,N} */
#define RPCIF_PHYCNT_WBUF2 BIT(4)
#define RPCIF_PHYCNT_WBUF BIT(2)
#define RPCIF_PHYCNT_PHYMEM(v) (((v) & 0x3) << 0)
#define RPCIF_PHYCNT_PHYMEM_MASK GENMASK(1, 0)
#define RPCIF_PHYOFFSET1 0x0080 /* R/W */
#define RPCIF_PHYOFFSET1_DDRTMG(v) (((v) & 0x3) << 28)
......@@ -147,8 +151,6 @@
#define RPCIF_PHYINT 0x0088 /* R/W */
#define RPCIF_PHYINT_WPVAL BIT(1)
#define RPCIF_DIRMAP_SIZE 0x4000000
static const struct regmap_range rpcif_volatile_ranges[] = {
regmap_reg_range(RPCIF_SMRDR0, RPCIF_SMRDR1),
regmap_reg_range(RPCIF_SMWDR0, RPCIF_SMWDR1),
......@@ -243,50 +245,74 @@ int rpcif_sw_init(struct rpcif *rpc, struct device *dev)
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dirmap");
rpc->dirmap = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(rpc->dirmap))
rpc->dirmap = NULL;
return PTR_ERR(rpc->dirmap);
rpc->size = resource_size(res);
rpc->type = (uintptr_t)of_device_get_match_data(dev);
rpc->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
return PTR_ERR_OR_ZERO(rpc->rstc);
}
EXPORT_SYMBOL(rpcif_sw_init);
void rpcif_hw_init(struct rpcif *rpc, bool hyperflash)
static void rpcif_rzg2l_timing_adjust_sdr(struct rpcif *rpc)
{
regmap_write(rpc->regmap, RPCIF_PHYWR, 0xa5390000);
regmap_write(rpc->regmap, RPCIF_PHYADD, 0x80000000);
regmap_write(rpc->regmap, RPCIF_PHYWR, 0x00008080);
regmap_write(rpc->regmap, RPCIF_PHYADD, 0x80000022);
regmap_write(rpc->regmap, RPCIF_PHYWR, 0x00008080);
regmap_write(rpc->regmap, RPCIF_PHYADD, 0x80000024);
regmap_update_bits(rpc->regmap, RPCIF_PHYCNT, RPCIF_PHYCNT_CKSEL(3),
RPCIF_PHYCNT_CKSEL(3));
regmap_write(rpc->regmap, RPCIF_PHYWR, 0x00000030);
regmap_write(rpc->regmap, RPCIF_PHYADD, 0x80000032);
}
int rpcif_hw_init(struct rpcif *rpc, bool hyperflash)
{
u32 dummy;
pm_runtime_get_sync(rpc->dev);
/*
* NOTE: The 0x260 are undocumented bits, but they must be set.
* RPCIF_PHYCNT_STRTIM is strobe timing adjustment bits,
* 0x0 : the delay is biggest,
* 0x1 : the delay is 2nd biggest,
* On H3 ES1.x, the value should be 0, while on others,
* the value should be 7.
*/
regmap_write(rpc->regmap, RPCIF_PHYCNT, RPCIF_PHYCNT_STRTIM(7) |
RPCIF_PHYCNT_PHYMEM(hyperflash ? 3 : 0) | 0x260);
/*
* NOTE: The 0x1511144 are undocumented bits, but they must be set
* for RPCIF_PHYOFFSET1.
* The 0x31 are undocumented bits, but they must be set
* for RPCIF_PHYOFFSET2.
*/
regmap_write(rpc->regmap, RPCIF_PHYOFFSET1, 0x1511144 |
RPCIF_PHYOFFSET1_DDRTMG(3));
regmap_write(rpc->regmap, RPCIF_PHYOFFSET2, 0x31 |
RPCIF_PHYOFFSET2_OCTTMG(4));
if (rpc->type == RPCIF_RZ_G2L) {
int ret;
ret = reset_control_reset(rpc->rstc);
if (ret)
return ret;
usleep_range(200, 300);
rpcif_rzg2l_timing_adjust_sdr(rpc);
}
regmap_update_bits(rpc->regmap, RPCIF_PHYCNT, RPCIF_PHYCNT_PHYMEM_MASK,
RPCIF_PHYCNT_PHYMEM(hyperflash ? 3 : 0));
if (rpc->type == RPCIF_RCAR_GEN3)
regmap_update_bits(rpc->regmap, RPCIF_PHYCNT,
RPCIF_PHYCNT_STRTIM(7), RPCIF_PHYCNT_STRTIM(7));
regmap_update_bits(rpc->regmap, RPCIF_PHYOFFSET1, RPCIF_PHYOFFSET1_DDRTMG(3),
RPCIF_PHYOFFSET1_DDRTMG(3));
regmap_update_bits(rpc->regmap, RPCIF_PHYOFFSET2, RPCIF_PHYOFFSET2_OCTTMG(7),
RPCIF_PHYOFFSET2_OCTTMG(4));
if (hyperflash)
regmap_update_bits(rpc->regmap, RPCIF_PHYINT,
RPCIF_PHYINT_WPVAL, 0);
regmap_write(rpc->regmap, RPCIF_CMNCR, RPCIF_CMNCR_SFDE |
RPCIF_CMNCR_MOIIO_HIZ | RPCIF_CMNCR_IOFV_HIZ |
RPCIF_CMNCR_BSZ(hyperflash ? 1 : 0));
if (rpc->type == RPCIF_RCAR_GEN3)
regmap_update_bits(rpc->regmap, RPCIF_CMNCR,
RPCIF_CMNCR_MOIIO(3) | RPCIF_CMNCR_BSZ(3),
RPCIF_CMNCR_MOIIO(3) |
RPCIF_CMNCR_BSZ(hyperflash ? 1 : 0));
else
regmap_update_bits(rpc->regmap, RPCIF_CMNCR,
RPCIF_CMNCR_MOIIO(3) | RPCIF_CMNCR_IOFV(3) |
RPCIF_CMNCR_BSZ(3),
RPCIF_CMNCR_MOIIO(1) | RPCIF_CMNCR_IOFV(2) |
RPCIF_CMNCR_BSZ(hyperflash ? 1 : 0));
/* Set RCF after BSZ update */
regmap_write(rpc->regmap, RPCIF_DRCR, RPCIF_DRCR_RCF);
/* Dummy read according to spec */
......@@ -297,6 +323,8 @@ void rpcif_hw_init(struct rpcif *rpc, bool hyperflash)
pm_runtime_put(rpc->dev);
rpc->bus_size = hyperflash ? 2 : 1;
return 0;
}
EXPORT_SYMBOL(rpcif_hw_init);
......@@ -588,8 +616,8 @@ static void memcpy_fromio_readw(void *to,
ssize_t rpcif_dirmap_read(struct rpcif *rpc, u64 offs, size_t len, void *buf)
{
loff_t from = offs & (RPCIF_DIRMAP_SIZE - 1);
size_t size = RPCIF_DIRMAP_SIZE - from;
loff_t from = offs & (rpc->size - 1);
size_t size = rpc->size - from;
if (len > size)
len = size;
......@@ -659,7 +687,8 @@ static int rpcif_remove(struct platform_device *pdev)
}
static const struct of_device_id rpcif_of_match[] = {
{ .compatible = "renesas,rcar-gen3-rpc-if", },
{ .compatible = "renesas,rcar-gen3-rpc-if", .data = (void *)RPCIF_RCAR_GEN3 },
{ .compatible = "renesas,rzg2l-rpc-if", .data = (void *)RPCIF_RZ_G2L },
{},
};
MODULE_DEVICE_TABLE(of, rpcif_of_match);
......
......@@ -15,6 +15,8 @@
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pm_opp.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
#include <linux/mmc/card.h>
......@@ -24,6 +26,8 @@
#include <linux/gpio/consumer.h>
#include <linux/ktime.h>
#include <soc/tegra/common.h>
#include "sdhci-pltfm.h"
#include "cqhci.h"
......@@ -743,7 +747,9 @@ static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
struct device *dev = mmc_dev(host->mmc);
unsigned long host_clk;
int err;
if (!clock)
return sdhci_set_clock(host, clock);
......@@ -761,7 +767,12 @@ static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
* from clk_get_rate() is used.
*/
host_clk = tegra_host->ddr_signaling ? clock * 2 : clock;
clk_set_rate(pltfm_host->clk, host_clk);
err = dev_pm_opp_set_rate(dev, host_clk);
if (err)
dev_err(dev, "failed to set clk rate to %luHz: %d\n",
host_clk, err);
tegra_host->curr_clk_rate = host_clk;
if (tegra_host->ddr_signaling)
host->max_clk = host_clk;
......@@ -1714,7 +1725,6 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
"failed to get clock\n");
goto err_clk_get;
}
clk_prepare_enable(clk);
pltfm_host->clk = clk;
tegra_host->rst = devm_reset_control_get_exclusive(&pdev->dev,
......@@ -1725,15 +1735,24 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
goto err_rst_get;
}
rc = reset_control_assert(tegra_host->rst);
rc = devm_tegra_core_dev_init_opp_table_common(&pdev->dev);
if (rc)
goto err_rst_get;
pm_runtime_enable(&pdev->dev);
rc = pm_runtime_resume_and_get(&pdev->dev);
if (rc)
goto err_pm_get;
rc = reset_control_assert(tegra_host->rst);
if (rc)
goto err_rst_assert;
usleep_range(2000, 4000);
rc = reset_control_deassert(tegra_host->rst);
if (rc)
goto err_rst_get;
goto err_rst_assert;
usleep_range(2000, 4000);
......@@ -1745,8 +1764,11 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
err_add_host:
reset_control_assert(tegra_host->rst);
err_rst_assert:
pm_runtime_put_sync_suspend(&pdev->dev);
err_pm_get:
pm_runtime_disable(&pdev->dev);
err_rst_get:
clk_disable_unprepare(pltfm_host->clk);
err_clk_get:
clk_disable_unprepare(tegra_host->tmclk);
err_power_req:
......@@ -1765,19 +1787,38 @@ static int sdhci_tegra_remove(struct platform_device *pdev)
reset_control_assert(tegra_host->rst);
usleep_range(2000, 4000);
clk_disable_unprepare(pltfm_host->clk);
clk_disable_unprepare(tegra_host->tmclk);
pm_runtime_put_sync_suspend(&pdev->dev);
pm_runtime_force_suspend(&pdev->dev);
clk_disable_unprepare(tegra_host->tmclk);
sdhci_pltfm_free(pdev);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int __maybe_unused sdhci_tegra_suspend(struct device *dev)
static int __maybe_unused sdhci_tegra_runtime_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
clk_disable_unprepare(pltfm_host->clk);
return 0;
}
static int __maybe_unused sdhci_tegra_runtime_resume(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
return clk_prepare_enable(pltfm_host->clk);
}
#ifdef CONFIG_PM_SLEEP
static int sdhci_tegra_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
int ret;
if (host->mmc->caps2 & MMC_CAP2_CQE) {
......@@ -1792,17 +1833,22 @@ static int __maybe_unused sdhci_tegra_suspend(struct device *dev)
return ret;
}
clk_disable_unprepare(pltfm_host->clk);
ret = pm_runtime_force_suspend(dev);
if (ret) {
sdhci_resume_host(host);
cqhci_resume(host->mmc);
return ret;
}
return 0;
}
static int __maybe_unused sdhci_tegra_resume(struct device *dev)
static int sdhci_tegra_resume(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
int ret;
ret = clk_prepare_enable(pltfm_host->clk);
ret = pm_runtime_force_resume(dev);
if (ret)
return ret;
......@@ -1821,13 +1867,16 @@ static int __maybe_unused sdhci_tegra_resume(struct device *dev)
suspend_host:
sdhci_suspend_host(host);
disable_clk:
clk_disable_unprepare(pltfm_host->clk);
pm_runtime_force_suspend(dev);
return ret;
}
#endif
static SIMPLE_DEV_PM_OPS(sdhci_tegra_dev_pm_ops, sdhci_tegra_suspend,
sdhci_tegra_resume);
static const struct dev_pm_ops sdhci_tegra_dev_pm_ops = {
SET_RUNTIME_PM_OPS(sdhci_tegra_runtime_suspend, sdhci_tegra_runtime_resume,
NULL)
SET_SYSTEM_SLEEP_PM_OPS(sdhci_tegra_suspend, sdhci_tegra_resume)
};
static struct platform_driver sdhci_tegra_driver = {
.driver = {
......
......@@ -130,7 +130,9 @@ static int rpcif_hb_probe(struct platform_device *pdev)
rpcif_enable_rpm(&hyperbus->rpc);
rpcif_hw_init(&hyperbus->rpc, true);
error = rpcif_hw_init(&hyperbus->rpc, true);
if (error)
return error;
hyperbus->hbdev.map.size = hyperbus->rpc.size;
hyperbus->hbdev.map.virt = hyperbus->rpc.dirmap;
......
......@@ -17,8 +17,11 @@
#include <linux/mtd/rawnand.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <soc/tegra/common.h>
#define COMMAND 0x00
#define COMMAND_GO BIT(31)
#define COMMAND_CLE BIT(30)
......@@ -1151,6 +1154,7 @@ static int tegra_nand_probe(struct platform_device *pdev)
return -ENOMEM;
ctrl->dev = &pdev->dev;
platform_set_drvdata(pdev, ctrl);
nand_controller_init(&ctrl->controller);
ctrl->controller.ops = &tegra_nand_controller_ops;
......@@ -1166,14 +1170,23 @@ static int tegra_nand_probe(struct platform_device *pdev)
if (IS_ERR(ctrl->clk))
return PTR_ERR(ctrl->clk);
err = clk_prepare_enable(ctrl->clk);
err = devm_tegra_core_dev_init_opp_table_common(&pdev->dev);
if (err)
return err;
/*
* This driver doesn't support active power management yet,
* so we will simply keep device resumed.
*/
pm_runtime_enable(&pdev->dev);
err = pm_runtime_resume_and_get(&pdev->dev);
if (err)
return err;
err = reset_control_reset(rst);
if (err) {
dev_err(ctrl->dev, "Failed to reset HW: %d\n", err);
goto err_disable_clk;
goto err_put_pm;
}
writel_relaxed(HWSTATUS_CMD_DEFAULT, ctrl->regs + HWSTATUS_CMD);
......@@ -1188,21 +1201,20 @@ static int tegra_nand_probe(struct platform_device *pdev)
dev_name(&pdev->dev), ctrl);
if (err) {
dev_err(ctrl->dev, "Failed to get IRQ: %d\n", err);
goto err_disable_clk;
goto err_put_pm;
}
writel_relaxed(DMA_MST_CTRL_IS_DONE, ctrl->regs + DMA_MST_CTRL);
err = tegra_nand_chips_init(ctrl->dev, ctrl);
if (err)
goto err_disable_clk;
platform_set_drvdata(pdev, ctrl);
goto err_put_pm;
return 0;
err_disable_clk:
clk_disable_unprepare(ctrl->clk);
err_put_pm:
pm_runtime_put_sync_suspend(ctrl->dev);
pm_runtime_force_suspend(ctrl->dev);
return err;
}
......@@ -1219,11 +1231,40 @@ static int tegra_nand_remove(struct platform_device *pdev)
nand_cleanup(chip);
pm_runtime_put_sync_suspend(ctrl->dev);
pm_runtime_force_suspend(ctrl->dev);
return 0;
}
static int __maybe_unused tegra_nand_runtime_resume(struct device *dev)
{
struct tegra_nand_controller *ctrl = dev_get_drvdata(dev);
int err;
err = clk_prepare_enable(ctrl->clk);
if (err) {
dev_err(dev, "Failed to enable clock: %d\n", err);
return err;
}
return 0;
}
static int __maybe_unused tegra_nand_runtime_suspend(struct device *dev)
{
struct tegra_nand_controller *ctrl = dev_get_drvdata(dev);
clk_disable_unprepare(ctrl->clk);
return 0;
}
static const struct dev_pm_ops tegra_nand_pm = {
SET_RUNTIME_PM_OPS(tegra_nand_runtime_suspend, tegra_nand_runtime_resume,
NULL)
};
static const struct of_device_id tegra_nand_of_match[] = {
{ .compatible = "nvidia,tegra20-nand" },
{ /* sentinel */ }
......@@ -1234,6 +1275,7 @@ static struct platform_driver tegra_nand_driver = {
.driver = {
.name = "tegra-nand",
.of_match_table = tegra_nand_of_match,
.pm = &tegra_nand_pm,
},
.probe = tegra_nand_probe,
.remove = tegra_nand_remove,
......
......@@ -42,12 +42,16 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/pm_opp.h>
#include <linux/pwm.h>
#include <linux/platform_device.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/reset.h>
#include <soc/tegra/common.h>
#define PWM_ENABLE (1 << 31)
#define PWM_DUTY_WIDTH 8
#define PWM_DUTY_SHIFT 16
......@@ -145,7 +149,7 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
required_clk_rate =
(NSEC_PER_SEC / period_ns) << PWM_DUTY_WIDTH;
err = clk_set_rate(pc->clk, required_clk_rate);
err = dev_pm_opp_set_rate(pc->dev, required_clk_rate);
if (err < 0)
return -EINVAL;
......@@ -181,8 +185,8 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
* before writing the register. Otherwise, keep it enabled.
*/
if (!pwm_is_enabled(pwm)) {
err = clk_prepare_enable(pc->clk);
if (err < 0)
err = pm_runtime_resume_and_get(pc->dev);
if (err)
return err;
} else
val |= PWM_ENABLE;
......@@ -193,7 +197,7 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
* If the PWM is not enabled, turn the clock off again to save power.
*/
if (!pwm_is_enabled(pwm))
clk_disable_unprepare(pc->clk);
pm_runtime_put(pc->dev);
return 0;
}
......@@ -204,8 +208,8 @@ static int tegra_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
int rc = 0;
u32 val;
rc = clk_prepare_enable(pc->clk);
if (rc < 0)
rc = pm_runtime_resume_and_get(pc->dev);
if (rc)
return rc;
val = pwm_readl(pc, pwm->hwpwm);
......@@ -224,7 +228,7 @@ static void tegra_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
val &= ~PWM_ENABLE;
pwm_writel(pc, pwm->hwpwm, val);
clk_disable_unprepare(pc->clk);
pm_runtime_put_sync(pc->dev);
}
static const struct pwm_ops tegra_pwm_ops = {
......@@ -256,11 +260,20 @@ static int tegra_pwm_probe(struct platform_device *pdev)
if (IS_ERR(pwm->clk))
return PTR_ERR(pwm->clk);
ret = devm_tegra_core_dev_init_opp_table_common(&pdev->dev);
if (ret)
return ret;
pm_runtime_enable(&pdev->dev);
ret = pm_runtime_resume_and_get(&pdev->dev);
if (ret)
return ret;
/* Set maximum frequency of the IP */
ret = clk_set_rate(pwm->clk, pwm->soc->max_frequency);
ret = dev_pm_opp_set_rate(pwm->dev, pwm->soc->max_frequency);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to set max frequency: %d\n", ret);
return ret;
goto put_pm;
}
/*
......@@ -278,7 +291,7 @@ static int tegra_pwm_probe(struct platform_device *pdev)
if (IS_ERR(pwm->rst)) {
ret = PTR_ERR(pwm->rst);
dev_err(&pdev->dev, "Reset control is not found: %d\n", ret);
return ret;
goto put_pm;
}
reset_control_deassert(pwm->rst);
......@@ -291,10 +304,16 @@ static int tegra_pwm_probe(struct platform_device *pdev)
if (ret < 0) {
dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
reset_control_assert(pwm->rst);
return ret;
goto put_pm;
}
pm_runtime_put(&pdev->dev);
return 0;
put_pm:
pm_runtime_put_sync_suspend(&pdev->dev);
pm_runtime_force_suspend(&pdev->dev);
return ret;
}
static int tegra_pwm_remove(struct platform_device *pdev)
......@@ -305,20 +324,44 @@ static int tegra_pwm_remove(struct platform_device *pdev)
reset_control_assert(pc->rst);
pm_runtime_force_suspend(&pdev->dev);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int tegra_pwm_suspend(struct device *dev)
static int __maybe_unused tegra_pwm_runtime_suspend(struct device *dev)
{
return pinctrl_pm_select_sleep_state(dev);
struct tegra_pwm_chip *pc = dev_get_drvdata(dev);
int err;
clk_disable_unprepare(pc->clk);
err = pinctrl_pm_select_sleep_state(dev);
if (err) {
clk_prepare_enable(pc->clk);
return err;
}
return 0;
}
static int tegra_pwm_resume(struct device *dev)
static int __maybe_unused tegra_pwm_runtime_resume(struct device *dev)
{
return pinctrl_pm_select_default_state(dev);
struct tegra_pwm_chip *pc = dev_get_drvdata(dev);
int err;
err = pinctrl_pm_select_default_state(dev);
if (err)
return err;
err = clk_prepare_enable(pc->clk);
if (err) {
pinctrl_pm_select_sleep_state(dev);
return err;
}
return 0;
}
#endif
static const struct tegra_pwm_soc tegra20_pwm_soc = {
.num_channels = 4,
......@@ -344,7 +387,10 @@ static const struct of_device_id tegra_pwm_of_match[] = {
MODULE_DEVICE_TABLE(of, tegra_pwm_of_match);
static const struct dev_pm_ops tegra_pwm_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(tegra_pwm_suspend, tegra_pwm_resume)
SET_RUNTIME_PM_OPS(tegra_pwm_runtime_suspend, tegra_pwm_runtime_resume,
NULL)
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
};
static struct platform_driver tegra_pwm_driver = {
......
......@@ -3,6 +3,7 @@ menu "SOC (System On Chip) specific Drivers"
source "drivers/soc/actions/Kconfig"
source "drivers/soc/amlogic/Kconfig"
source "drivers/soc/apple/Kconfig"
source "drivers/soc/aspeed/Kconfig"
source "drivers/soc/atmel/Kconfig"
source "drivers/soc/bcm/Kconfig"
......
......@@ -4,6 +4,7 @@
#
obj-$(CONFIG_ARCH_ACTIONS) += actions/
obj-$(CONFIG_ARCH_APPLE) += apple/
obj-y += aspeed/
obj-$(CONFIG_ARCH_AT91) += atmel/
obj-y += bcm/
......
# SPDX-License-Identifier: GPL-2.0-only
if ARCH_APPLE || COMPILE_TEST
menu "Apple SoC drivers"
config APPLE_PMGR_PWRSTATE
bool "Apple SoC PMGR power state control"
depends on PM
select REGMAP
select MFD_SYSCON
select PM_GENERIC_DOMAINS
select RESET_CONTROLLER
default ARCH_APPLE
help
The PMGR block in Apple SoCs provides high-level power state
controls for SoC devices. This driver manages them through the
generic power domain framework, and also provides reset support.
endmenu
endif
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_APPLE_PMGR_PWRSTATE) += apple-pmgr-pwrstate.o
// SPDX-License-Identifier: GPL-2.0-only OR MIT
/*
* Apple SoC PMGR device power state driver
*
* Copyright The Asahi Linux Contributors
*/
#include <linux/bitops.h>
#include <linux/bitfield.h>
#include <linux/err.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
#include <linux/reset-controller.h>
#include <linux/module.h>
#define APPLE_PMGR_RESET BIT(31)
#define APPLE_PMGR_AUTO_ENABLE BIT(28)
#define APPLE_PMGR_PS_AUTO GENMASK(27, 24)
#define APPLE_PMGR_PS_MIN GENMASK(19, 16)
#define APPLE_PMGR_PARENT_OFF BIT(11)
#define APPLE_PMGR_DEV_DISABLE BIT(10)
#define APPLE_PMGR_WAS_CLKGATED BIT(9)
#define APPLE_PMGR_WAS_PWRGATED BIT(8)
#define APPLE_PMGR_PS_ACTUAL GENMASK(7, 4)
#define APPLE_PMGR_PS_TARGET GENMASK(3, 0)
#define APPLE_PMGR_FLAGS (APPLE_PMGR_WAS_CLKGATED | APPLE_PMGR_WAS_PWRGATED)
#define APPLE_PMGR_PS_ACTIVE 0xf
#define APPLE_PMGR_PS_CLKGATE 0x4
#define APPLE_PMGR_PS_PWRGATE 0x0
#define APPLE_PMGR_PS_SET_TIMEOUT 100
#define APPLE_PMGR_RESET_TIME 1
struct apple_pmgr_ps {
struct device *dev;
struct generic_pm_domain genpd;
struct reset_controller_dev rcdev;
struct regmap *regmap;
u32 offset;
u32 min_state;
};
#define genpd_to_apple_pmgr_ps(_genpd) container_of(_genpd, struct apple_pmgr_ps, genpd)
#define rcdev_to_apple_pmgr_ps(_rcdev) container_of(_rcdev, struct apple_pmgr_ps, rcdev)
static int apple_pmgr_ps_set(struct generic_pm_domain *genpd, u32 pstate, bool auto_enable)
{
int ret;
struct apple_pmgr_ps *ps = genpd_to_apple_pmgr_ps(genpd);
u32 reg;
ret = regmap_read(ps->regmap, ps->offset, &reg);
if (ret < 0)
return ret;
/* Resets are synchronous, and only work if the device is powered and clocked. */
if (reg & APPLE_PMGR_RESET && pstate != APPLE_PMGR_PS_ACTIVE)
dev_err(ps->dev, "PS %s: powering off with RESET active\n",
genpd->name);
reg &= ~(APPLE_PMGR_AUTO_ENABLE | APPLE_PMGR_FLAGS | APPLE_PMGR_PS_TARGET);
reg |= FIELD_PREP(APPLE_PMGR_PS_TARGET, pstate);
dev_dbg(ps->dev, "PS %s: pwrstate = 0x%x: 0x%x\n", genpd->name, pstate, reg);
regmap_write(ps->regmap, ps->offset, reg);
ret = regmap_read_poll_timeout_atomic(
ps->regmap, ps->offset, reg,
(FIELD_GET(APPLE_PMGR_PS_ACTUAL, reg) == pstate), 1,
APPLE_PMGR_PS_SET_TIMEOUT);
if (ret < 0)
dev_err(ps->dev, "PS %s: Failed to reach power state 0x%x (now: 0x%x)\n",
genpd->name, pstate, reg);
if (auto_enable) {
/* Not all devices implement this; this is a no-op where not implemented. */
reg &= ~APPLE_PMGR_FLAGS;
reg |= APPLE_PMGR_AUTO_ENABLE;
regmap_write(ps->regmap, ps->offset, reg);
}
return ret;
}
static bool apple_pmgr_ps_is_active(struct apple_pmgr_ps *ps)
{
u32 reg = 0;
regmap_read(ps->regmap, ps->offset, &reg);
/*
* We consider domains as active if they are actually on, or if they have auto-PM
* enabled and the intended target is on.
*/
return (FIELD_GET(APPLE_PMGR_PS_ACTUAL, reg) == APPLE_PMGR_PS_ACTIVE ||
(FIELD_GET(APPLE_PMGR_PS_TARGET, reg) == APPLE_PMGR_PS_ACTIVE &&
reg & APPLE_PMGR_AUTO_ENABLE));
}
static int apple_pmgr_ps_power_on(struct generic_pm_domain *genpd)
{
return apple_pmgr_ps_set(genpd, APPLE_PMGR_PS_ACTIVE, true);
}
static int apple_pmgr_ps_power_off(struct generic_pm_domain *genpd)
{
return apple_pmgr_ps_set(genpd, APPLE_PMGR_PS_PWRGATE, false);
}
static int apple_pmgr_reset_assert(struct reset_controller_dev *rcdev, unsigned long id)
{
struct apple_pmgr_ps *ps = rcdev_to_apple_pmgr_ps(rcdev);
mutex_lock(&ps->genpd.mlock);
if (ps->genpd.status == GENPD_STATE_OFF)
dev_err(ps->dev, "PS 0x%x: asserting RESET while powered down\n", ps->offset);
dev_dbg(ps->dev, "PS 0x%x: assert reset\n", ps->offset);
/* Quiesce device before asserting reset */
regmap_update_bits(ps->regmap, ps->offset, APPLE_PMGR_FLAGS | APPLE_PMGR_DEV_DISABLE,
APPLE_PMGR_DEV_DISABLE);
regmap_update_bits(ps->regmap, ps->offset, APPLE_PMGR_FLAGS | APPLE_PMGR_RESET,
APPLE_PMGR_RESET);
mutex_unlock(&ps->genpd.mlock);
return 0;
}
static int apple_pmgr_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id)
{
struct apple_pmgr_ps *ps = rcdev_to_apple_pmgr_ps(rcdev);
mutex_lock(&ps->genpd.mlock);
dev_dbg(ps->dev, "PS 0x%x: deassert reset\n", ps->offset);
regmap_update_bits(ps->regmap, ps->offset, APPLE_PMGR_FLAGS | APPLE_PMGR_RESET, 0);
regmap_update_bits(ps->regmap, ps->offset, APPLE_PMGR_FLAGS | APPLE_PMGR_DEV_DISABLE, 0);
if (ps->genpd.status == GENPD_STATE_OFF)
dev_err(ps->dev, "PS 0x%x: RESET was deasserted while powered down\n", ps->offset);
mutex_unlock(&ps->genpd.mlock);
return 0;
}
static int apple_pmgr_reset_reset(struct reset_controller_dev *rcdev, unsigned long id)
{
int ret;
ret = apple_pmgr_reset_assert(rcdev, id);
if (ret)
return ret;
usleep_range(APPLE_PMGR_RESET_TIME, 2 * APPLE_PMGR_RESET_TIME);
return apple_pmgr_reset_deassert(rcdev, id);
}
static int apple_pmgr_reset_status(struct reset_controller_dev *rcdev, unsigned long id)
{
struct apple_pmgr_ps *ps = rcdev_to_apple_pmgr_ps(rcdev);
u32 reg = 0;
regmap_read(ps->regmap, ps->offset, &reg);
return !!(reg & APPLE_PMGR_RESET);
}
const struct reset_control_ops apple_pmgr_reset_ops = {
.assert = apple_pmgr_reset_assert,
.deassert = apple_pmgr_reset_deassert,
.reset = apple_pmgr_reset_reset,
.status = apple_pmgr_reset_status,
};
static int apple_pmgr_reset_xlate(struct reset_controller_dev *rcdev,
const struct of_phandle_args *reset_spec)
{
return 0;
}
static int apple_pmgr_ps_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *node = dev->of_node;
struct apple_pmgr_ps *ps;
struct regmap *regmap;
struct of_phandle_iterator it;
int ret;
const char *name;
bool active;
regmap = syscon_node_to_regmap(node->parent);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
ps = devm_kzalloc(dev, sizeof(*ps), GFP_KERNEL);
if (!ps)
return -ENOMEM;
ps->dev = dev;
ps->regmap = regmap;
ret = of_property_read_string(node, "label", &name);
if (ret < 0) {
dev_err(dev, "missing label property\n");
return ret;
}
ret = of_property_read_u32(node, "reg", &ps->offset);
if (ret < 0) {
dev_err(dev, "missing reg property\n");
return ret;
}
ps->genpd.name = name;
ps->genpd.power_on = apple_pmgr_ps_power_on;
ps->genpd.power_off = apple_pmgr_ps_power_off;
ret = of_property_read_u32(node, "apple,min-state", &ps->min_state);
if (ret == 0 && ps->min_state <= APPLE_PMGR_PS_ACTIVE)
regmap_update_bits(regmap, ps->offset, APPLE_PMGR_FLAGS | APPLE_PMGR_PS_MIN,
FIELD_PREP(APPLE_PMGR_PS_MIN, ps->min_state));
active = apple_pmgr_ps_is_active(ps);
if (of_property_read_bool(node, "apple,always-on")) {
ps->genpd.flags |= GENPD_FLAG_ALWAYS_ON;
if (!active) {
dev_warn(dev, "always-on domain %s is not on at boot\n", name);
/* Turn it on so pm_genpd_init does not fail */
active = apple_pmgr_ps_power_on(&ps->genpd) == 0;
}
}
/* Turn on auto-PM if the domain is already on */
if (active)
regmap_update_bits(regmap, ps->offset, APPLE_PMGR_FLAGS | APPLE_PMGR_AUTO_ENABLE,
APPLE_PMGR_AUTO_ENABLE);
ret = pm_genpd_init(&ps->genpd, NULL, !active);
if (ret < 0) {
dev_err(dev, "pm_genpd_init failed\n");
return ret;
}
ret = of_genpd_add_provider_simple(node, &ps->genpd);
if (ret < 0) {
dev_err(dev, "of_genpd_add_provider_simple failed\n");
return ret;
}
of_for_each_phandle(&it, ret, node, "power-domains", "#power-domain-cells", -1) {
struct of_phandle_args parent, child;
parent.np = it.node;
parent.args_count = of_phandle_iterator_args(&it, parent.args, MAX_PHANDLE_ARGS);
child.np = node;
child.args_count = 0;
ret = of_genpd_add_subdomain(&parent, &child);
if (ret == -EPROBE_DEFER) {
of_node_put(parent.np);
goto err_remove;
} else if (ret < 0) {
dev_err(dev, "failed to add to parent domain: %d (%s -> %s)\n",
ret, it.node->name, node->name);
of_node_put(parent.np);
goto err_remove;
}
}
/*
* Do not participate in regular PM; parent power domains are handled via the
* genpd hierarchy.
*/
pm_genpd_remove_device(dev);
ps->rcdev.owner = THIS_MODULE;
ps->rcdev.nr_resets = 1;
ps->rcdev.ops = &apple_pmgr_reset_ops;
ps->rcdev.of_node = dev->of_node;
ps->rcdev.of_reset_n_cells = 0;
ps->rcdev.of_xlate = apple_pmgr_reset_xlate;
ret = devm_reset_controller_register(dev, &ps->rcdev);
if (ret < 0)
goto err_remove;
return 0;
err_remove:
of_genpd_del_provider(node);
pm_genpd_remove(&ps->genpd);
return ret;
}
static const struct of_device_id apple_pmgr_ps_of_match[] = {
{ .compatible = "apple,pmgr-pwrstate" },
{}
};
MODULE_DEVICE_TABLE(of, apple_pmgr_ps_of_match);
static struct platform_driver apple_pmgr_ps_driver = {
.probe = apple_pmgr_ps_probe,
.driver = {
.name = "apple-pmgr-pwrstate",
.of_match_table = apple_pmgr_ps_of_match,
},
};
MODULE_AUTHOR("Hector Martin <marcan@marcan.st>");
MODULE_DESCRIPTION("PMGR power state driver for Apple SoCs");
MODULE_LICENSE("GPL v2");
module_platform_driver(apple_pmgr_ps_driver);
......@@ -377,7 +377,7 @@ static int imx_pgc_power_down(struct generic_pm_domain *genpd)
}
}
pm_runtime_put(domain->dev);
pm_runtime_put_sync_suspend(domain->dev);
return 0;
......@@ -734,6 +734,7 @@ static const struct imx_pgc_domain imx8mm_pgc_domains[] = {
.map = IMX8MM_VPUH1_A53_DOMAIN,
},
.pgc = BIT(IMX8MM_PGC_VPUH1),
.keep_clocks = true,
},
[IMX8MM_POWER_DOMAIN_DISPMIX] = {
......@@ -840,6 +841,32 @@ static const struct imx_pgc_domain imx8mn_pgc_domains[] = {
.hskack = IMX8MN_GPUMIX_HSK_PWRDNACKN,
},
.pgc = BIT(IMX8MN_PGC_GPUMIX),
.keep_clocks = true,
},
[IMX8MN_POWER_DOMAIN_DISPMIX] = {
.genpd = {
.name = "dispmix",
},
.bits = {
.pxx = IMX8MN_DISPMIX_SW_Pxx_REQ,
.map = IMX8MN_DISPMIX_A53_DOMAIN,
.hskreq = IMX8MN_DISPMIX_HSK_PWRDNREQN,
.hskack = IMX8MN_DISPMIX_HSK_PWRDNACKN,
},
.pgc = BIT(IMX8MN_PGC_DISPMIX),
.keep_clocks = true,
},
[IMX8MN_POWER_DOMAIN_MIPI] = {
.genpd = {
.name = "mipi",
},
.bits = {
.pxx = IMX8MN_MIPI_SW_Pxx_REQ,
.map = IMX8MN_MIPI_A53_DOMAIN,
},
.pgc = BIT(IMX8MN_PGC_MIPI),
},
};
......
......@@ -14,6 +14,7 @@
#include <linux/clk.h>
#include <dt-bindings/power/imx8mm-power.h>
#include <dt-bindings/power/imx8mn-power.h>
#define BLK_SFT_RSTN 0x0
#define BLK_CLK_EN 0x4
......@@ -517,6 +518,77 @@ static const struct imx8m_blk_ctrl_data imx8mm_disp_blk_ctl_dev_data = {
.num_domains = ARRAY_SIZE(imx8mm_disp_blk_ctl_domain_data),
};
static int imx8mn_disp_power_notifier(struct notifier_block *nb,
unsigned long action, void *data)
{
struct imx8m_blk_ctrl *bc = container_of(nb, struct imx8m_blk_ctrl,
power_nb);
if (action != GENPD_NOTIFY_ON && action != GENPD_NOTIFY_PRE_OFF)
return NOTIFY_OK;
/* Enable bus clock and deassert bus reset */
regmap_set_bits(bc->regmap, BLK_CLK_EN, BIT(8));
regmap_set_bits(bc->regmap, BLK_SFT_RSTN, BIT(8));
/*
* On power up we have no software backchannel to the GPC to
* wait for the ADB handshake to happen, so we just delay for a
* bit. On power down the GPC driver waits for the handshake.
*/
if (action == GENPD_NOTIFY_ON)
udelay(5);
return NOTIFY_OK;
}
static const struct imx8m_blk_ctrl_domain_data imx8mn_disp_blk_ctl_domain_data[] = {
[IMX8MN_DISPBLK_PD_MIPI_DSI] = {
.name = "dispblk-mipi-dsi",
.clk_names = (const char *[]){ "dsi-pclk", "dsi-ref", },
.num_clks = 2,
.gpc_name = "mipi-dsi",
.rst_mask = BIT(0) | BIT(1),
.clk_mask = BIT(0) | BIT(1),
.mipi_phy_rst_mask = BIT(17),
},
[IMX8MN_DISPBLK_PD_MIPI_CSI] = {
.name = "dispblk-mipi-csi",
.clk_names = (const char *[]){ "csi-aclk", "csi-pclk" },
.num_clks = 2,
.gpc_name = "mipi-csi",
.rst_mask = BIT(2) | BIT(3),
.clk_mask = BIT(2) | BIT(3),
.mipi_phy_rst_mask = BIT(16),
},
[IMX8MN_DISPBLK_PD_LCDIF] = {
.name = "dispblk-lcdif",
.clk_names = (const char *[]){ "lcdif-axi", "lcdif-apb", "lcdif-pix", },
.num_clks = 3,
.gpc_name = "lcdif",
.rst_mask = BIT(4) | BIT(5),
.clk_mask = BIT(4) | BIT(5),
},
[IMX8MN_DISPBLK_PD_ISI] = {
.name = "dispblk-isi",
.clk_names = (const char *[]){ "disp_axi", "disp_apb", "disp_axi_root",
"disp_apb_root"},
.num_clks = 4,
.gpc_name = "isi",
.rst_mask = BIT(6) | BIT(7),
.clk_mask = BIT(6) | BIT(7),
},
};
static const struct imx8m_blk_ctrl_data imx8mn_disp_blk_ctl_dev_data = {
.max_reg = 0x84,
.power_notifier_fn = imx8mn_disp_power_notifier,
.domains = imx8mn_disp_blk_ctl_domain_data,
.num_domains = ARRAY_SIZE(imx8mn_disp_blk_ctl_domain_data),
};
static const struct of_device_id imx8m_blk_ctrl_of_match[] = {
{
.compatible = "fsl,imx8mm-vpu-blk-ctrl",
......@@ -524,7 +596,10 @@ static const struct of_device_id imx8m_blk_ctrl_of_match[] = {
}, {
.compatible = "fsl,imx8mm-disp-blk-ctrl",
.data = &imx8mm_disp_blk_ctl_dev_data
} ,{
}, {
.compatible = "fsl,imx8mn-disp-blk-ctrl",
.data = &imx8mn_disp_blk_ctl_dev_data
}, {
/* Sentinel */
}
};
......
......@@ -1010,7 +1010,7 @@ static int cpr_interpolate(const struct corner *corner, int step_volt,
return corner->uV;
temp = f_diff * (uV_high - uV_low);
do_div(temp, f_high - f_low);
temp = div64_ul(temp, f_high - f_low);
/*
* max_volt_scale has units of uV/MHz while freq values
......
......@@ -195,6 +195,28 @@ static const struct llcc_slice_config sm8250_data[] = {
{ LLCC_WRCACHE, 31, 256, 1, 1, 0xfff, 0x0, 0, 0, 0, 0, 1, 0 },
};
static const struct llcc_slice_config sm8350_data[] = {
{ LLCC_CPUSS, 1, 3072, 1, 1, 0xfff, 0x0, 0, 0, 0, 0, 1, 1 },
{ LLCC_VIDSC0, 2, 512, 3, 1, 0xfff, 0x0, 0, 0, 0, 0, 1, 0 },
{ LLCC_AUDIO, 6, 1024, 1, 1, 0xfff, 0x0, 0, 0, 0, 0, 0, 0 },
{ LLCC_MDMHPGRW, 7, 1024, 3, 0, 0xfff, 0x0, 0, 0, 0, 0, 1, 0 },
{ LLCC_MODHW, 9, 1024, 1, 1, 0xfff, 0x0, 0, 0, 0, 0, 1, 0 },
{ LLCC_CMPT, 10, 3072, 1, 1, 0xfff, 0x0, 0, 0, 0, 0, 1, 0 },
{ LLCC_GPUHTW, 11, 1024, 1, 1, 0xfff, 0x0, 0, 0, 0, 0, 1, 0 },
{ LLCC_GPU, 12, 1024, 1, 0, 0xfff, 0x0, 0, 0, 0, 1, 1, 0 },
{ LLCC_MMUHWT, 13, 1024, 1, 1, 0xfff, 0x0, 0, 0, 0, 0, 0, 1 },
{ LLCC_DISP, 16, 3072, 2, 1, 0xfff, 0x0, 0, 0, 0, 0, 1, 0 },
{ LLCC_MDMPNG, 21, 1024, 0, 1, 0xf, 0x0, 0, 0, 0, 0, 1, 0 },
{ LLCC_AUDHW, 22, 1024, 1, 1, 0xfff, 0x0, 0, 0, 0, 0, 1, 0 },
{ LLCC_CVP, 28, 512, 3, 1, 0xfff, 0x0, 0, 0, 0, 0, 1, 0 },
{ LLCC_MODPE, 29, 256, 1, 1, 0xf, 0x0, 0, 0, 0, 0, 1, 0 },
{ LLCC_APTCM, 30, 1024, 3, 1, 0x0, 0x1, 1, 0, 0, 0, 1, 0 },
{ LLCC_WRCACHE, 31, 512, 1, 1, 0xfff, 0x0, 0, 0, 0, 0, 0, 1 },
{ LLCC_CVPFW, 17, 512, 1, 0, 0xfff, 0x0, 0, 0, 0, 0, 1, 0 },
{ LLCC_CPUSS1, 3, 1024, 1, 1, 0xfff, 0x0, 0, 0, 0, 0, 1, 0 },
{ LLCC_CPUHWT, 5, 512, 1, 1, 0xfff, 0x0, 0, 0, 0, 0, 0, 1 },
};
static const struct qcom_llcc_config sc7180_cfg = {
.sct_data = sc7180_data,
.size = ARRAY_SIZE(sc7180_data),
......@@ -228,6 +250,11 @@ static const struct qcom_llcc_config sm8250_cfg = {
.size = ARRAY_SIZE(sm8250_data),
};
static const struct qcom_llcc_config sm8350_cfg = {
.sct_data = sm8350_data,
.size = ARRAY_SIZE(sm8350_data),
};
static struct llcc_drv_data *drv_data = (void *) -EPROBE_DEFER;
/**
......@@ -644,6 +671,7 @@ static const struct of_device_id qcom_llcc_of_match[] = {
{ .compatible = "qcom,sm6350-llcc", .data = &sm6350_cfg },
{ .compatible = "qcom,sm8150-llcc", .data = &sm8150_cfg },
{ .compatible = "qcom,sm8250-llcc", .data = &sm8250_cfg },
{ .compatible = "qcom,sm8350-llcc", .data = &sm8350_cfg },
{ }
};
......
......@@ -352,7 +352,7 @@ static int qmp_cdev_set_cur_state(struct thermal_cooling_device *cdev,
return ret;
}
static struct thermal_cooling_device_ops qmp_cooling_device_ops = {
static const struct thermal_cooling_device_ops qmp_cooling_device_ops = {
.get_max_state = qmp_cdev_get_max_state,
.get_cur_state = qmp_cdev_get_cur_state,
.set_cur_state = qmp_cdev_set_cur_state,
......
......@@ -237,6 +237,15 @@ static const struct stats_config rpm_data = {
.subsystem_stats_in_smem = false,
};
/* Older RPM firmwares have the stats at a fixed offset instead */
static const struct stats_config rpm_data_dba0 = {
.stats_offset = 0xdba0,
.num_records = 2,
.appended_stats_avail = true,
.dynamic_offset = false,
.subsystem_stats_in_smem = false,
};
static const struct stats_config rpmh_data = {
.stats_offset = 0x48,
.num_records = 3,
......@@ -246,6 +255,10 @@ static const struct stats_config rpmh_data = {
};
static const struct of_device_id qcom_stats_table[] = {
{ .compatible = "qcom,apq8084-rpm-stats", .data = &rpm_data_dba0 },
{ .compatible = "qcom,msm8226-rpm-stats", .data = &rpm_data_dba0 },
{ .compatible = "qcom,msm8916-rpm-stats", .data = &rpm_data_dba0 },
{ .compatible = "qcom,msm8974-rpm-stats", .data = &rpm_data_dba0 },
{ .compatible = "qcom,rpm-stats", .data = &rpm_data },
{ .compatible = "qcom,rpmh-stats", .data = &rpmh_data },
{ }
......
......@@ -96,7 +96,7 @@ static void qmi_recv_del_server(struct qmi_handle *qmi,
* @node: id of the dying node
*
* Signals the client that all previously registered services on this node are
* now gone and then calls the bye callback to allow the client client further
* now gone and then calls the bye callback to allow the client further
* cleaning up resources associated with this remote.
*/
static void qmi_recv_bye(struct qmi_handle *qmi,
......
......@@ -691,7 +691,7 @@ static int find_slots(struct tcs_group *tcs, const struct tcs_request *msg,
* @drv: The controller.
* @msg: The data to be written to the controller.
*
* This should only be called for for sleep/wake state, never active-only
* This should only be called for sleep/wake state, never active-only
* state.
*
* The caller must ensure that no other RPMH actions are happening and the
......
This diff is collapsed.
......@@ -102,7 +102,6 @@ struct rpmpd {
const bool active_only;
unsigned int corner;
bool enabled;
const char *res_name;
const int res_type;
const int res_id;
struct qcom_smd_rpm *rpm;
......@@ -396,6 +395,45 @@ static const struct rpmpd_desc sm6115_desc = {
.max_state = RPM_SMD_LEVEL_TURBO_NO_CPR,
};
/* sm6125 RPM Power domains */
DEFINE_RPMPD_PAIR(sm6125, vddcx, vddcx_ao, RWCX, LEVEL, 0);
DEFINE_RPMPD_VFL(sm6125, vddcx_vfl, RWCX, 0);
DEFINE_RPMPD_PAIR(sm6125, vddmx, vddmx_ao, RWMX, LEVEL, 0);
DEFINE_RPMPD_VFL(sm6125, vddmx_vfl, RWMX, 0);
static struct rpmpd *sm6125_rpmpds[] = {
[SM6125_VDDCX] = &sm6125_vddcx,
[SM6125_VDDCX_AO] = &sm6125_vddcx_ao,
[SM6125_VDDCX_VFL] = &sm6125_vddcx_vfl,
[SM6125_VDDMX] = &sm6125_vddmx,
[SM6125_VDDMX_AO] = &sm6125_vddmx_ao,
[SM6125_VDDMX_VFL] = &sm6125_vddmx_vfl,
};
static const struct rpmpd_desc sm6125_desc = {
.rpmpds = sm6125_rpmpds,
.num_pds = ARRAY_SIZE(sm6125_rpmpds),
.max_state = RPM_SMD_LEVEL_BINNING,
};
static struct rpmpd *qcm2290_rpmpds[] = {
[QCM2290_VDDCX] = &sm6115_vddcx,
[QCM2290_VDDCX_AO] = &sm6115_vddcx_ao,
[QCM2290_VDDCX_VFL] = &sm6115_vddcx_vfl,
[QCM2290_VDDMX] = &sm6115_vddmx,
[QCM2290_VDDMX_AO] = &sm6115_vddmx_ao,
[QCM2290_VDDMX_VFL] = &sm6115_vddmx_vfl,
[QCM2290_VDD_LPI_CX] = &sm6115_vdd_lpi_cx,
[QCM2290_VDD_LPI_MX] = &sm6115_vdd_lpi_mx,
};
static const struct rpmpd_desc qcm2290_desc = {
.rpmpds = qcm2290_rpmpds,
.num_pds = ARRAY_SIZE(qcm2290_rpmpds),
.max_state = RPM_SMD_LEVEL_TURBO_NO_CPR,
};
static const struct of_device_id rpmpd_match_table[] = {
{ .compatible = "qcom,mdm9607-rpmpd", .data = &mdm9607_desc },
{ .compatible = "qcom,msm8916-rpmpd", .data = &msm8916_desc },
......@@ -405,9 +443,11 @@ static const struct of_device_id rpmpd_match_table[] = {
{ .compatible = "qcom,msm8994-rpmpd", .data = &msm8994_desc },
{ .compatible = "qcom,msm8996-rpmpd", .data = &msm8996_desc },
{ .compatible = "qcom,msm8998-rpmpd", .data = &msm8998_desc },
{ .compatible = "qcom,qcm2290-rpmpd", .data = &qcm2290_desc },
{ .compatible = "qcom,qcs404-rpmpd", .data = &qcs404_desc },
{ .compatible = "qcom,sdm660-rpmpd", .data = &sdm660_desc },
{ .compatible = "qcom,sm6115-rpmpd", .data = &sm6115_desc },
{ .compatible = "qcom,sm6125-rpmpd", .data = &sm6125_desc },
{ }
};
MODULE_DEVICE_TABLE(of, rpmpd_match_table);
......
......@@ -85,7 +85,7 @@
#define SMEM_GLOBAL_HOST 0xfffe
/* Max number of processors/hosts in a system */
#define SMEM_HOST_COUNT 14
#define SMEM_HOST_COUNT 15
/**
* struct smem_proc_comm - proc_comm communication struct (legacy)
......
......@@ -313,8 +313,11 @@ static const struct soc_id soc_id[] = {
{ 421, "IPQ6000" },
{ 422, "IPQ6010" },
{ 425, "SC7180" },
{ 434, "SM6350" },
{ 453, "IPQ6005" },
{ 455, "QRB5165" },
{ 457, "SM8450" },
{ 459, "SM7225" },
};
static const char *socinfo_machine(struct device *dev, unsigned int id)
......
......@@ -235,6 +235,13 @@ config ARCH_R8A77961
This enables support for the Renesas R-Car M3-W+ SoC.
This includes different gradings like R-Car M3e and M3e-2G.
config ARCH_R8A779F0
bool "ARM64 Platform support for R-Car S4-8"
select ARCH_RCAR_GEN3
select SYSC_R8A779F0
help
This enables support for the Renesas R-Car S4-8 SoC.
config ARCH_R8A77980
bool "ARM64 Platform support for R-Car V3H"
select ARCH_RCAR_GEN3
......@@ -297,6 +304,9 @@ config RST_RCAR
config SYSC_RCAR
bool "System Controller support for R-Car" if COMPILE_TEST
config SYSC_RCAR_GEN4
bool "System Controller support for R-Car Gen4" if COMPILE_TEST
config SYSC_R8A77995
bool "System Controller support for R-Car D3" if COMPILE_TEST
select SYSC_RCAR
......@@ -337,6 +347,10 @@ config SYSC_R8A77961
bool "System Controller support for R-Car M3-W+" if COMPILE_TEST
select SYSC_RCAR
config SYSC_R8A779F0
bool "System Controller support for R-Car S4-8" if COMPILE_TEST
select SYSC_RCAR_GEN4
config SYSC_R8A7792
bool "System Controller support for R-Car V2H" if COMPILE_TEST
select SYSC_RCAR
......@@ -351,6 +365,7 @@ config SYSC_R8A77970
config SYSC_R8A779A0
bool "System Controller support for R-Car V3U" if COMPILE_TEST
select SYSC_RCAR_GEN4
config SYSC_RMOBILE
bool "System Controller support for R-Mobile" if COMPILE_TEST
......
......@@ -25,6 +25,7 @@ obj-$(CONFIG_SYSC_R8A77980) += r8a77980-sysc.o
obj-$(CONFIG_SYSC_R8A77990) += r8a77990-sysc.o
obj-$(CONFIG_SYSC_R8A77995) += r8a77995-sysc.o
obj-$(CONFIG_SYSC_R8A779A0) += r8a779a0-sysc.o
obj-$(CONFIG_SYSC_R8A779F0) += r8a779f0-sysc.o
ifdef CONFIG_SMP
obj-$(CONFIG_ARCH_R9A06G032) += r9a06g032-smp.o
endif
......@@ -32,4 +33,5 @@ endif
# Family
obj-$(CONFIG_RST_RCAR) += rcar-rst.o
obj-$(CONFIG_SYSC_RCAR) += rcar-sysc.o
obj-$(CONFIG_SYSC_RCAR_GEN4) += rcar-gen4-sysc.o
obj-$(CONFIG_SYSC_RMOBILE) += rmobile-sysc.o
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0
/*
* Renesas R-Car S4-8 System Controller
*
* Copyright (C) 2021 Renesas Electronics Corp.
*/
#include <linux/bits.h>
#include <linux/clk/renesas.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/of_address.h>
#include <linux/pm_domain.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include <dt-bindings/power/r8a779f0-sysc.h>
#include "rcar-gen4-sysc.h"
static struct rcar_gen4_sysc_area r8a779f0_areas[] __initdata = {
{ "always-on", R8A779F0_PD_ALWAYS_ON, -1, PD_ALWAYS_ON },
{ "a3e0", R8A779F0_PD_A3E0, R8A779F0_PD_ALWAYS_ON, PD_SCU },
{ "a3e1", R8A779F0_PD_A3E1, R8A779F0_PD_ALWAYS_ON, PD_SCU },
{ "a2e0d0", R8A779F0_PD_A2E0D0, R8A779F0_PD_A3E0, PD_SCU },
{ "a2e0d1", R8A779F0_PD_A2E0D1, R8A779F0_PD_A3E0, PD_SCU },
{ "a2e1d0", R8A779F0_PD_A2E1D0, R8A779F0_PD_A3E1, PD_SCU },
{ "a2e1d1", R8A779F0_PD_A2E1D1, R8A779F0_PD_A3E1, PD_SCU },
{ "a1e0d0c0", R8A779F0_PD_A1E0D0C0, R8A779F0_PD_A2E0D0, PD_CPU_NOCR },
{ "a1e0d0c1", R8A779F0_PD_A1E0D0C1, R8A779F0_PD_A2E0D0, PD_CPU_NOCR },
{ "a1e0d1c0", R8A779F0_PD_A1E0D1C0, R8A779F0_PD_A2E0D1, PD_CPU_NOCR },
{ "a1e0d1c1", R8A779F0_PD_A1E0D1C1, R8A779F0_PD_A2E0D1, PD_CPU_NOCR },
{ "a1e1d0c0", R8A779F0_PD_A1E1D0C0, R8A779F0_PD_A2E1D0, PD_CPU_NOCR },
{ "a1e1d0c1", R8A779F0_PD_A1E1D0C1, R8A779F0_PD_A2E1D0, PD_CPU_NOCR },
{ "a1e1d1c0", R8A779F0_PD_A1E1D1C0, R8A779F0_PD_A2E1D1, PD_CPU_NOCR },
{ "a1e1d1c1", R8A779F0_PD_A1E1D1C1, R8A779F0_PD_A2E1D1, PD_CPU_NOCR },
};
const struct rcar_gen4_sysc_info r8a779f0_sysc_info __initconst = {
.areas = r8a779f0_areas,
.num_areas = ARRAY_SIZE(r8a779f0_areas),
};
// SPDX-License-Identifier: GPL-2.0
/*
* R-Car Gen4 SYSC Power management support
*
* Copyright (C) 2021 Renesas Electronics Corp.
*/
#include <linux/bits.h>
#include <linux/clk/renesas.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/of_address.h>
#include <linux/pm_domain.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include "rcar-gen4-sysc.h"
/* SYSC Common */
#define SYSCSR 0x000 /* SYSC Status Register */
#define SYSCPONSR(x) (0x800 + ((x) * 0x4)) /* Power-ON Status Register 0 */
#define SYSCPOFFSR(x) (0x808 + ((x) * 0x4)) /* Power-OFF Status Register */
#define SYSCISCR(x) (0x810 + ((x) * 0x4)) /* Interrupt Status/Clear Register */
#define SYSCIER(x) (0x820 + ((x) * 0x4)) /* Interrupt Enable Register */
#define SYSCIMR(x) (0x830 + ((x) * 0x4)) /* Interrupt Mask Register */
/* Power Domain Registers */
#define PDRSR(n) (0x1000 + ((n) * 0x40))
#define PDRONCR(n) (0x1004 + ((n) * 0x40))
#define PDROFFCR(n) (0x1008 + ((n) * 0x40))
#define PDRESR(n) (0x100C + ((n) * 0x40))
/* PWRON/PWROFF */
#define PWRON_PWROFF BIT(0) /* Power-ON/OFF request */
/* PDRESR */
#define PDRESR_ERR BIT(0)
/* PDRSR */
#define PDRSR_OFF BIT(0) /* Power-OFF state */
#define PDRSR_ON BIT(4) /* Power-ON state */
#define PDRSR_OFF_STATE BIT(8) /* Processing Power-OFF sequence */
#define PDRSR_ON_STATE BIT(12) /* Processing Power-ON sequence */
#define SYSCSR_BUSY GENMASK(1, 0) /* All bit sets is not busy */
#define SYSCSR_TIMEOUT 10000
#define SYSCSR_DELAY_US 10
#define PDRESR_RETRIES 1000
#define PDRESR_DELAY_US 10
#define SYSCISR_TIMEOUT 10000
#define SYSCISR_DELAY_US 10
#define RCAR_GEN4_PD_ALWAYS_ON 64
#define NUM_DOMAINS_EACH_REG BITS_PER_TYPE(u32)
static void __iomem *rcar_gen4_sysc_base;
static DEFINE_SPINLOCK(rcar_gen4_sysc_lock); /* SMP CPUs + I/O devices */
static int rcar_gen4_sysc_pwr_on_off(u8 pdr, bool on)
{
unsigned int reg_offs;
u32 val;
int ret;
if (on)
reg_offs = PDRONCR(pdr);
else
reg_offs = PDROFFCR(pdr);
/* Wait until SYSC is ready to accept a power request */
ret = readl_poll_timeout_atomic(rcar_gen4_sysc_base + SYSCSR, val,
(val & SYSCSR_BUSY) == SYSCSR_BUSY,
SYSCSR_DELAY_US, SYSCSR_TIMEOUT);
if (ret < 0)
return -EAGAIN;
/* Submit power shutoff or power resume request */
iowrite32(PWRON_PWROFF, rcar_gen4_sysc_base + reg_offs);
return 0;
}
static int clear_irq_flags(unsigned int reg_idx, unsigned int isr_mask)
{
u32 val;
int ret;
iowrite32(isr_mask, rcar_gen4_sysc_base + SYSCISCR(reg_idx));
ret = readl_poll_timeout_atomic(rcar_gen4_sysc_base + SYSCISCR(reg_idx),
val, !(val & isr_mask),
SYSCISR_DELAY_US, SYSCISR_TIMEOUT);
if (ret < 0) {
pr_err("\n %s : Can not clear IRQ flags in SYSCISCR", __func__);
return -EIO;
}
return 0;
}
static int rcar_gen4_sysc_power(u8 pdr, bool on)
{
unsigned int isr_mask;
unsigned int reg_idx, bit_idx;
unsigned int status;
unsigned long flags;
int ret = 0;
u32 val;
int k;
spin_lock_irqsave(&rcar_gen4_sysc_lock, flags);
reg_idx = pdr / NUM_DOMAINS_EACH_REG;
bit_idx = pdr % NUM_DOMAINS_EACH_REG;
isr_mask = BIT(bit_idx);
/*
* The interrupt source needs to be enabled, but masked, to prevent the
* CPU from receiving it.
*/
iowrite32(ioread32(rcar_gen4_sysc_base + SYSCIER(reg_idx)) | isr_mask,
rcar_gen4_sysc_base + SYSCIER(reg_idx));
iowrite32(ioread32(rcar_gen4_sysc_base + SYSCIMR(reg_idx)) | isr_mask,
rcar_gen4_sysc_base + SYSCIMR(reg_idx));
ret = clear_irq_flags(reg_idx, isr_mask);
if (ret)
goto out;
/* Submit power shutoff or resume request until it was accepted */
for (k = 0; k < PDRESR_RETRIES; k++) {
ret = rcar_gen4_sysc_pwr_on_off(pdr, on);
if (ret)
goto out;
status = ioread32(rcar_gen4_sysc_base + PDRESR(pdr));
if (!(status & PDRESR_ERR))
break;
udelay(PDRESR_DELAY_US);
}
if (k == PDRESR_RETRIES) {
ret = -EIO;
goto out;
}
/* Wait until the power shutoff or resume request has completed * */
ret = readl_poll_timeout_atomic(rcar_gen4_sysc_base + SYSCISCR(reg_idx),
val, (val & isr_mask),
SYSCISR_DELAY_US, SYSCISR_TIMEOUT);
if (ret < 0) {
ret = -EIO;
goto out;
}
/* Clear interrupt flags */
ret = clear_irq_flags(reg_idx, isr_mask);
if (ret)
goto out;
out:
spin_unlock_irqrestore(&rcar_gen4_sysc_lock, flags);
pr_debug("sysc power %s domain %d: %08x -> %d\n", on ? "on" : "off",
pdr, ioread32(rcar_gen4_sysc_base + SYSCISCR(reg_idx)), ret);
return ret;
}
static bool rcar_gen4_sysc_power_is_off(u8 pdr)
{
unsigned int st;
st = ioread32(rcar_gen4_sysc_base + PDRSR(pdr));
if (st & PDRSR_OFF)
return true;
return false;
}
struct rcar_gen4_sysc_pd {
struct generic_pm_domain genpd;
u8 pdr;
unsigned int flags;
char name[];
};
static inline struct rcar_gen4_sysc_pd *to_rcar_gen4_pd(struct generic_pm_domain *d)
{
return container_of(d, struct rcar_gen4_sysc_pd, genpd);
}
static int rcar_gen4_sysc_pd_power_off(struct generic_pm_domain *genpd)
{
struct rcar_gen4_sysc_pd *pd = to_rcar_gen4_pd(genpd);
pr_debug("%s: %s\n", __func__, genpd->name);
return rcar_gen4_sysc_power(pd->pdr, false);
}
static int rcar_gen4_sysc_pd_power_on(struct generic_pm_domain *genpd)
{
struct rcar_gen4_sysc_pd *pd = to_rcar_gen4_pd(genpd);
pr_debug("%s: %s\n", __func__, genpd->name);
return rcar_gen4_sysc_power(pd->pdr, true);
}
static int __init rcar_gen4_sysc_pd_setup(struct rcar_gen4_sysc_pd *pd)
{
struct generic_pm_domain *genpd = &pd->genpd;
const char *name = pd->genpd.name;
int error;
if (pd->flags & PD_CPU) {
/*
* This domain contains a CPU core and therefore it should
* only be turned off if the CPU is not in use.
*/
pr_debug("PM domain %s contains %s\n", name, "CPU");
genpd->flags |= GENPD_FLAG_ALWAYS_ON;
} else if (pd->flags & PD_SCU) {
/*
* This domain contains an SCU and cache-controller, and
* therefore it should only be turned off if the CPU cores are
* not in use.
*/
pr_debug("PM domain %s contains %s\n", name, "SCU");
genpd->flags |= GENPD_FLAG_ALWAYS_ON;
} else if (pd->flags & PD_NO_CR) {
/*
* This domain cannot be turned off.
*/
genpd->flags |= GENPD_FLAG_ALWAYS_ON;
}
if (!(pd->flags & (PD_CPU | PD_SCU))) {
/* Enable Clock Domain for I/O devices */
genpd->flags |= GENPD_FLAG_PM_CLK | GENPD_FLAG_ACTIVE_WAKEUP;
genpd->attach_dev = cpg_mssr_attach_dev;
genpd->detach_dev = cpg_mssr_detach_dev;
}
genpd->power_off = rcar_gen4_sysc_pd_power_off;
genpd->power_on = rcar_gen4_sysc_pd_power_on;
if (pd->flags & (PD_CPU | PD_NO_CR)) {
/* Skip CPUs (handled by SMP code) and areas without control */
pr_debug("%s: Not touching %s\n", __func__, genpd->name);
goto finalize;
}
if (!rcar_gen4_sysc_power_is_off(pd->pdr)) {
pr_debug("%s: %s is already powered\n", __func__, genpd->name);
goto finalize;
}
rcar_gen4_sysc_power(pd->pdr, true);
finalize:
error = pm_genpd_init(genpd, &simple_qos_governor, false);
if (error)
pr_err("Failed to init PM domain %s: %d\n", name, error);
return error;
}
static const struct of_device_id rcar_gen4_sysc_matches[] __initconst = {
#ifdef CONFIG_SYSC_R8A779A0
{ .compatible = "renesas,r8a779a0-sysc", .data = &r8a779a0_sysc_info },
#endif
#ifdef CONFIG_SYSC_R8A779F0
{ .compatible = "renesas,r8a779f0-sysc", .data = &r8a779f0_sysc_info },
#endif
{ /* sentinel */ }
};
struct rcar_gen4_pm_domains {
struct genpd_onecell_data onecell_data;
struct generic_pm_domain *domains[RCAR_GEN4_PD_ALWAYS_ON + 1];
};
static struct genpd_onecell_data *rcar_gen4_sysc_onecell_data;
static int __init rcar_gen4_sysc_pd_init(void)
{
const struct rcar_gen4_sysc_info *info;
const struct of_device_id *match;
struct rcar_gen4_pm_domains *domains;
struct device_node *np;
void __iomem *base;
unsigned int i;
int error;
np = of_find_matching_node_and_match(NULL, rcar_gen4_sysc_matches, &match);
if (!np)
return -ENODEV;
info = match->data;
base = of_iomap(np, 0);
if (!base) {
pr_warn("%pOF: Cannot map regs\n", np);
error = -ENOMEM;
goto out_put;
}
rcar_gen4_sysc_base = base;
domains = kzalloc(sizeof(*domains), GFP_KERNEL);
if (!domains) {
error = -ENOMEM;
goto out_put;
}
domains->onecell_data.domains = domains->domains;
domains->onecell_data.num_domains = ARRAY_SIZE(domains->domains);
rcar_gen4_sysc_onecell_data = &domains->onecell_data;
for (i = 0; i < info->num_areas; i++) {
const struct rcar_gen4_sysc_area *area = &info->areas[i];
struct rcar_gen4_sysc_pd *pd;
size_t n;
if (!area->name) {
/* Skip NULLified area */
continue;
}
n = strlen(area->name) + 1;
pd = kzalloc(sizeof(*pd) + n, GFP_KERNEL);
if (!pd) {
error = -ENOMEM;
goto out_put;
}
memcpy(pd->name, area->name, n);
pd->genpd.name = pd->name;
pd->pdr = area->pdr;
pd->flags = area->flags;
error = rcar_gen4_sysc_pd_setup(pd);
if (error)
goto out_put;
domains->domains[area->pdr] = &pd->genpd;
if (area->parent < 0)
continue;
error = pm_genpd_add_subdomain(domains->domains[area->parent],
&pd->genpd);
if (error) {
pr_warn("Failed to add PM subdomain %s to parent %u\n",
area->name, area->parent);
goto out_put;
}
}
error = of_genpd_add_provider_onecell(np, &domains->onecell_data);
out_put:
of_node_put(np);
return error;
}
early_initcall(rcar_gen4_sysc_pd_init);
/* SPDX-License-Identifier: GPL-2.0 */
/*
* R-Car Gen4 System Controller
*
* Copyright (C) 2021 Renesas Electronics Corp.
*/
#ifndef __SOC_RENESAS_RCAR_GEN4_SYSC_H__
#define __SOC_RENESAS_RCAR_GEN4_SYSC_H__
#include <linux/types.h>
/*
* Power Domain flags
*/
#define PD_CPU BIT(0) /* Area contains main CPU core */
#define PD_SCU BIT(1) /* Area contains SCU and L2 cache */
#define PD_NO_CR BIT(2) /* Area lacks PWR{ON,OFF}CR registers */
#define PD_CPU_NOCR (PD_CPU | PD_NO_CR) /* CPU area lacks CR */
#define PD_ALWAYS_ON PD_NO_CR /* Always-on area */
/*
* Description of a Power Area
*/
struct rcar_gen4_sysc_area {
const char *name;
u8 pdr; /* PDRn */
int parent; /* -1 if none */
unsigned int flags; /* See PD_* */
};
/*
* SoC-specific Power Area Description
*/
struct rcar_gen4_sysc_info {
const struct rcar_gen4_sysc_area *areas;
unsigned int num_areas;
};
extern const struct rcar_gen4_sysc_info r8a779a0_sysc_info;
extern const struct rcar_gen4_sysc_info r8a779f0_sysc_info;
#endif /* __SOC_RENESAS_RCAR_GEN4_SYSC_H__ */
......@@ -13,15 +13,43 @@
#define WDTRSTCR_RESET 0xA55A0002
#define WDTRSTCR 0x0054
#define CR7BAR 0x0070
#define CR7BAREN BIT(4)
#define CR7BAR_MASK 0xFFFC0000
static void __iomem *rcar_rst_base;
static u32 saved_mode __initdata;
static int (*rcar_rst_set_rproc_boot_addr_func)(u64 boot_addr);
static int rcar_rst_enable_wdt_reset(void __iomem *base)
{
iowrite32(WDTRSTCR_RESET, base + WDTRSTCR);
return 0;
}
/*
* Most of the R-Car Gen3 SoCs have an ARM Realtime Core.
* Firmware boot address has to be set in CR7BAR before
* starting the realtime core.
* Boot address must be aligned on a 256k boundary.
*/
static int rcar_rst_set_gen3_rproc_boot_addr(u64 boot_addr)
{
if (boot_addr & ~(u64)CR7BAR_MASK) {
pr_err("Invalid boot address got %llx\n", boot_addr);
return -EINVAL;
}
iowrite32(boot_addr, rcar_rst_base + CR7BAR);
iowrite32(boot_addr | CR7BAREN, rcar_rst_base + CR7BAR);
return 0;
}
struct rst_config {
unsigned int modemr; /* Mode Monitoring Register Offset */
int (*configure)(void __iomem *base); /* Platform specific config */
int (*set_rproc_boot_addr)(u64 boot_addr);
};
static const struct rst_config rcar_rst_gen1 __initconst = {
......@@ -35,9 +63,10 @@ static const struct rst_config rcar_rst_gen2 __initconst = {
static const struct rst_config rcar_rst_gen3 __initconst = {
.modemr = 0x60,
.set_rproc_boot_addr = rcar_rst_set_gen3_rproc_boot_addr,
};
static const struct rst_config rcar_rst_r8a779a0 __initconst = {
static const struct rst_config rcar_rst_gen4 __initconst = {
.modemr = 0x00, /* MODEMR0 and it has CPG related bits */
};
......@@ -71,14 +100,12 @@ static const struct of_device_id rcar_rst_matches[] __initconst = {
{ .compatible = "renesas,r8a77980-rst", .data = &rcar_rst_gen3 },
{ .compatible = "renesas,r8a77990-rst", .data = &rcar_rst_gen3 },
{ .compatible = "renesas,r8a77995-rst", .data = &rcar_rst_gen3 },
/* R-Car V3U */
{ .compatible = "renesas,r8a779a0-rst", .data = &rcar_rst_r8a779a0 },
/* R-Car Gen4 */
{ .compatible = "renesas,r8a779a0-rst", .data = &rcar_rst_gen4 },
{ .compatible = "renesas,r8a779f0-rst", .data = &rcar_rst_gen4 },
{ /* sentinel */ }
};
static void __iomem *rcar_rst_base __initdata;
static u32 saved_mode __initdata;
static int __init rcar_rst_init(void)
{
const struct of_device_id *match;
......@@ -100,6 +127,8 @@ static int __init rcar_rst_init(void)
rcar_rst_base = base;
cfg = match->data;
rcar_rst_set_rproc_boot_addr_func = cfg->set_rproc_boot_addr;
saved_mode = ioread32(base + cfg->modemr);
if (cfg->configure) {
error = cfg->configure(base);
......@@ -130,3 +159,12 @@ int __init rcar_rst_read_mode_pins(u32 *mode)
*mode = saved_mode;
return 0;
}
int rcar_rst_set_rproc_boot_addr(u64 boot_addr)
{
if (!rcar_rst_set_rproc_boot_addr_func)
return -EIO;
return rcar_rst_set_rproc_boot_addr_func(boot_addr);
}
EXPORT_SYMBOL_GPL(rcar_rst_set_rproc_boot_addr);
......@@ -33,6 +33,10 @@ static const struct renesas_family fam_rcar_gen3 __initconst __maybe_unused = {
.reg = 0xfff00044, /* PRR (Product Register) */
};
static const struct renesas_family fam_rcar_gen4 __initconst __maybe_unused = {
.name = "R-Car Gen4",
};
static const struct renesas_family fam_rmobile __initconst __maybe_unused = {
.name = "R-Mobile",
.reg = 0xe600101c, /* CCCR (Common Chip Code Register) */
......@@ -214,6 +218,11 @@ static const struct renesas_soc soc_rcar_v3u __initconst __maybe_unused = {
.id = 0x59,
};
static const struct renesas_soc soc_rcar_s4 __initconst __maybe_unused = {
.family = &fam_rcar_gen4,
.id = 0x5a,
};
static const struct renesas_soc soc_shmobile_ag5 __initconst __maybe_unused = {
.family = &fam_shmobile,
.id = 0x37,
......@@ -319,6 +328,9 @@ static const struct of_device_id renesas_socs[] __initconst = {
#ifdef CONFIG_ARCH_R8A779A0
{ .compatible = "renesas,r8a779a0", .data = &soc_rcar_v3u },
#endif
#ifdef CONFIG_ARCH_R8A779F0
{ .compatible = "renesas,r8a779f0", .data = &soc_rcar_s4 },
#endif
#if defined(CONFIG_ARCH_R9A07G044)
{ .compatible = "renesas,r9a07g044", .data = &soc_rz_g2l },
#endif
......@@ -328,94 +340,92 @@ static const struct of_device_id renesas_socs[] __initconst = {
{ /* sentinel */ }
};
struct renesas_id {
unsigned int offset;
u32 mask;
};
static const struct renesas_id id_bsid __initconst = {
.offset = 0,
.mask = 0xff0000,
/*
* TODO: Upper 4 bits of BSID are for chip version, but the format is
* not known at this time so we don't know how to specify eshi and eslo
*/
};
static const struct renesas_id id_rzg2l __initconst = {
.offset = 0xa04,
.mask = 0xfffffff,
};
static const struct renesas_id id_prr __initconst = {
.offset = 0,
.mask = 0xff00,
};
static const struct of_device_id renesas_ids[] __initconst = {
{ .compatible = "renesas,bsid", .data = &id_bsid },
{ .compatible = "renesas,r9a07g044-sysc", .data = &id_rzg2l },
{ .compatible = "renesas,prr", .data = &id_prr },
{ /* sentinel */ }
};
static int __init renesas_soc_init(void)
{
struct soc_device_attribute *soc_dev_attr;
unsigned int product, eshi = 0, eslo;
const struct renesas_family *family;
const struct of_device_id *match;
const struct renesas_soc *soc;
const struct renesas_id *id;
void __iomem *chipid = NULL;
struct soc_device *soc_dev;
struct device_node *np;
unsigned int product, eshi = 0, eslo;
const char *soc_id;
match = of_match_node(renesas_socs, of_root);
if (!match)
return -ENODEV;
soc_id = strchr(match->compatible, ',') + 1;
soc = match->data;
family = soc->family;
np = of_find_compatible_node(NULL, NULL, "renesas,bsid");
np = of_find_matching_node_and_match(NULL, renesas_ids, &match);
if (np) {
id = match->data;
chipid = of_iomap(np, 0);
of_node_put(np);
if (chipid) {
product = readl(chipid);
iounmap(chipid);
if (soc->id && ((product >> 16) & 0xff) != soc->id) {
pr_warn("SoC mismatch (product = 0x%x)\n",
product);
return -ENODEV;
}
}
/*
* TODO: Upper 4 bits of BSID are for chip version, but the
* format is not known at this time so we don't know how to
* specify eshi and eslo
*/
goto done;
} else if (soc->id && family->reg) {
/* Try hardcoded CCCR/PRR fallback */
id = &id_prr;
chipid = ioremap(family->reg, 4);
}
np = of_find_compatible_node(NULL, NULL, "renesas,r9a07g044-sysc");
if (np) {
chipid = of_iomap(np, 0);
of_node_put(np);
if (chipid) {
product = readl(chipid + id->offset);
iounmap(chipid);
if (chipid) {
product = readl(chipid + 0x0a04);
iounmap(chipid);
if (id == &id_prr) {
/* R-Car M3-W ES1.1 incorrectly identifies as ES2.0 */
if ((product & 0x7fff) == 0x5210)
product ^= 0x11;
/* R-Car M3-W ES1.3 incorrectly identifies as ES2.1 */
if ((product & 0x7fff) == 0x5211)
product ^= 0x12;
if (soc->id && (product & 0xfffffff) != soc->id) {
pr_warn("SoC mismatch (product = 0x%x)\n",
product);
return -ENODEV;
}
eshi = ((product >> 4) & 0x0f) + 1;
eslo = product & 0xf;
}
goto done;
}
/* Try PRR first, then hardcoded fallback */
np = of_find_compatible_node(NULL, NULL, "renesas,prr");
if (np) {
chipid = of_iomap(np, 0);
of_node_put(np);
} else if (soc->id && family->reg) {
chipid = ioremap(family->reg, 4);
}
if (chipid) {
product = readl(chipid);
iounmap(chipid);
/* R-Car M3-W ES1.1 incorrectly identifies as ES2.0 */
if ((product & 0x7fff) == 0x5210)
product ^= 0x11;
/* R-Car M3-W ES1.3 incorrectly identifies as ES2.1 */
if ((product & 0x7fff) == 0x5211)
product ^= 0x12;
if (soc->id && ((product >> 8) & 0xff) != soc->id) {
if (soc->id &&
((product & id->mask) >> __ffs(id->mask)) != soc->id) {
pr_warn("SoC mismatch (product = 0x%x)\n", product);
return -ENODEV;
}
eshi = ((product >> 4) & 0x0f) + 1;
eslo = product & 0xf;
}
done:
soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
if (!soc_dev_attr)
return -ENOMEM;
......@@ -425,8 +435,7 @@ static int __init renesas_soc_init(void)
of_node_put(np);
soc_dev_attr->family = kstrdup_const(family->name, GFP_KERNEL);
soc_dev_attr->soc_id = kstrdup_const(strchr(match->compatible, ',') + 1,
GFP_KERNEL);
soc_dev_attr->soc_id = kstrdup_const(soc_id, GFP_KERNEL);
if (eshi)
soc_dev_attr->revision = kasprintf(GFP_KERNEL, "ES%u.%u", eshi,
eslo);
......
......@@ -23,6 +23,20 @@ config EXYNOS_CHIPID
Support for Samsung Exynos SoC ChipID and Adaptive Supply Voltage.
This driver can also be built as module (exynos_chipid).
config EXYNOS_USI
tristate "Exynos USI (Universal Serial Interface) driver"
default ARCH_EXYNOS && ARM64
depends on ARCH_EXYNOS || COMPILE_TEST
select MFD_SYSCON
help
Enable support for USI block. USI (Universal Serial Interface) is an
IP-core found in modern Samsung Exynos SoCs, like Exynos850 and
ExynosAutoV0. USI block can be configured to provide one of the
following serial protocols: UART, SPI or High Speed I2C.
This driver allows one to configure USI for desired protocol, which
is usually done in USI node in Device Tree.
config EXYNOS_PMU
bool "Exynos PMU controller driver" if COMPILE_TEST
depends on ARCH_EXYNOS || ((ARM || ARM64) && COMPILE_TEST)
......
......@@ -4,6 +4,8 @@ obj-$(CONFIG_EXYNOS_ASV_ARM) += exynos5422-asv.o
obj-$(CONFIG_EXYNOS_CHIPID) += exynos_chipid.o
exynos_chipid-y += exynos-chipid.o exynos-asv.o
obj-$(CONFIG_EXYNOS_USI) += exynos-usi.o
obj-$(CONFIG_EXYNOS_PMU) += exynos-pmu.o
obj-$(CONFIG_EXYNOS_PMU_ARM_DRIVERS) += exynos3250-pmu.o exynos4-pmu.o \
......
......@@ -42,6 +42,7 @@ static const struct exynos_soc_id {
unsigned int id;
} soc_ids[] = {
/* List ordered by SoC name */
/* Compatible with: samsung,exynos4210-chipid */
{ "EXYNOS3250", 0xE3472000 },
{ "EXYNOS4210", 0x43200000 }, /* EVT0 revision */
{ "EXYNOS4210", 0x43210000 },
......@@ -55,6 +56,8 @@ static const struct exynos_soc_id {
{ "EXYNOS5440", 0xE5440000 },
{ "EXYNOS5800", 0xE5422000 },
{ "EXYNOS7420", 0xE7420000 },
/* Compatible with: samsung,exynos850-chipid */
{ "EXYNOS7885", 0xE7885000 },
{ "EXYNOS850", 0xE3830000 },
{ "EXYNOSAUTOV9", 0xAAA80000 },
};
......
......@@ -94,6 +94,8 @@ static const struct of_device_id exynos_pmu_of_device_ids[] = {
.compatible = "samsung,exynos5433-pmu",
}, {
.compatible = "samsung,exynos7-pmu",
}, {
.compatible = "samsung,exynos850-pmu",
},
{ /*sentinel*/ },
};
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2021 Linaro Ltd.
* Author: Sam Protsenko <semen.protsenko@linaro.org>
*
* Samsung Exynos USI driver (Universal Serial Interface).
*/
#include <linux/clk.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <dt-bindings/soc/samsung,exynos-usi.h>
/* USIv2: System Register: SW_CONF register bits */
#define USI_V2_SW_CONF_NONE 0x0
#define USI_V2_SW_CONF_UART BIT(0)
#define USI_V2_SW_CONF_SPI BIT(1)
#define USI_V2_SW_CONF_I2C BIT(2)
#define USI_V2_SW_CONF_MASK (USI_V2_SW_CONF_UART | USI_V2_SW_CONF_SPI | \
USI_V2_SW_CONF_I2C)
/* USIv2: USI register offsets */
#define USI_CON 0x04
#define USI_OPTION 0x08
/* USIv2: USI register bits */
#define USI_CON_RESET BIT(0)
#define USI_OPTION_CLKREQ_ON BIT(1)
#define USI_OPTION_CLKSTOP_ON BIT(2)
enum exynos_usi_ver {
USI_VER2 = 2,
};
struct exynos_usi_variant {
enum exynos_usi_ver ver; /* USI IP-core version */
unsigned int sw_conf_mask; /* SW_CONF mask for all protocols */
size_t min_mode; /* first index in exynos_usi_modes[] */
size_t max_mode; /* last index in exynos_usi_modes[] */
size_t num_clks; /* number of clocks to assert */
const char * const *clk_names; /* clock names to assert */
};
struct exynos_usi {
struct device *dev;
void __iomem *regs; /* USI register map */
struct clk_bulk_data *clks; /* USI clocks */
size_t mode; /* current USI SW_CONF mode index */
bool clkreq_on; /* always provide clock to IP */
/* System Register */
struct regmap *sysreg; /* System Register map */
unsigned int sw_conf; /* SW_CONF register offset in sysreg */
const struct exynos_usi_variant *data;
};
struct exynos_usi_mode {
const char *name; /* mode name */
unsigned int val; /* mode register value */
};
static const struct exynos_usi_mode exynos_usi_modes[] = {
[USI_V2_NONE] = { .name = "none", .val = USI_V2_SW_CONF_NONE },
[USI_V2_UART] = { .name = "uart", .val = USI_V2_SW_CONF_UART },
[USI_V2_SPI] = { .name = "spi", .val = USI_V2_SW_CONF_SPI },
[USI_V2_I2C] = { .name = "i2c", .val = USI_V2_SW_CONF_I2C },
};
static const char * const exynos850_usi_clk_names[] = { "pclk", "ipclk" };
static const struct exynos_usi_variant exynos850_usi_data = {
.ver = USI_VER2,
.sw_conf_mask = USI_V2_SW_CONF_MASK,
.min_mode = USI_V2_NONE,
.max_mode = USI_V2_I2C,
.num_clks = ARRAY_SIZE(exynos850_usi_clk_names),
.clk_names = exynos850_usi_clk_names,
};
static const struct of_device_id exynos_usi_dt_match[] = {
{
.compatible = "samsung,exynos850-usi",
.data = &exynos850_usi_data,
},
{ } /* sentinel */
};
MODULE_DEVICE_TABLE(of, exynos_usi_dt_match);
/**
* exynos_usi_set_sw_conf - Set USI block configuration mode
* @usi: USI driver object
* @mode: Mode index
*
* Select underlying serial protocol (UART/SPI/I2C) in USI IP-core.
*
* Return: 0 on success, or negative error code on failure.
*/
static int exynos_usi_set_sw_conf(struct exynos_usi *usi, size_t mode)
{
unsigned int val;
int ret;
if (mode < usi->data->min_mode || mode > usi->data->max_mode)
return -EINVAL;
val = exynos_usi_modes[mode].val;
ret = regmap_update_bits(usi->sysreg, usi->sw_conf,
usi->data->sw_conf_mask, val);
if (ret)
return ret;
usi->mode = mode;
dev_dbg(usi->dev, "protocol: %s\n", exynos_usi_modes[usi->mode].name);
return 0;
}
/**
* exynos_usi_enable - Initialize USI block
* @usi: USI driver object
*
* USI IP-core start state is "reset" (on startup and after CPU resume). This
* routine enables the USI block by clearing the reset flag. It also configures
* HWACG behavior (needed e.g. for UART Rx). It should be performed before
* underlying protocol becomes functional.
*
* Return: 0 on success, or negative error code on failure.
*/
static int exynos_usi_enable(const struct exynos_usi *usi)
{
u32 val;
int ret;
ret = clk_bulk_prepare_enable(usi->data->num_clks, usi->clks);
if (ret)
return ret;
/* Enable USI block */
val = readl(usi->regs + USI_CON);
val &= ~USI_CON_RESET;
writel(val, usi->regs + USI_CON);
udelay(1);
/* Continuously provide the clock to USI IP w/o gating */
if (usi->clkreq_on) {
val = readl(usi->regs + USI_OPTION);
val &= ~USI_OPTION_CLKSTOP_ON;
val |= USI_OPTION_CLKREQ_ON;
writel(val, usi->regs + USI_OPTION);
}
clk_bulk_disable_unprepare(usi->data->num_clks, usi->clks);
return ret;
}
static int exynos_usi_configure(struct exynos_usi *usi)
{
int ret;
ret = exynos_usi_set_sw_conf(usi, usi->mode);
if (ret)
return ret;
if (usi->data->ver == USI_VER2)
return exynos_usi_enable(usi);
return 0;
}
static int exynos_usi_parse_dt(struct device_node *np, struct exynos_usi *usi)
{
int ret;
u32 mode;
ret = of_property_read_u32(np, "samsung,mode", &mode);
if (ret)
return ret;
if (mode < usi->data->min_mode || mode > usi->data->max_mode)
return -EINVAL;
usi->mode = mode;
usi->sysreg = syscon_regmap_lookup_by_phandle(np, "samsung,sysreg");
if (IS_ERR(usi->sysreg))
return PTR_ERR(usi->sysreg);
ret = of_property_read_u32_index(np, "samsung,sysreg", 1,
&usi->sw_conf);
if (ret)
return ret;
usi->clkreq_on = of_property_read_bool(np, "samsung,clkreq-on");
return 0;
}
static int exynos_usi_get_clocks(struct exynos_usi *usi)
{
const size_t num = usi->data->num_clks;
struct device *dev = usi->dev;
size_t i;
if (num == 0)
return 0;
usi->clks = devm_kcalloc(dev, num, sizeof(*usi->clks), GFP_KERNEL);
if (!usi->clks)
return -ENOMEM;
for (i = 0; i < num; ++i)
usi->clks[i].id = usi->data->clk_names[i];
return devm_clk_bulk_get(dev, num, usi->clks);
}
static int exynos_usi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct exynos_usi *usi;
int ret;
usi = devm_kzalloc(dev, sizeof(*usi), GFP_KERNEL);
if (!usi)
return -ENOMEM;
usi->dev = dev;
platform_set_drvdata(pdev, usi);
usi->data = of_device_get_match_data(dev);
if (!usi->data)
return -EINVAL;
ret = exynos_usi_parse_dt(np, usi);
if (ret)
return ret;
ret = exynos_usi_get_clocks(usi);
if (ret)
return ret;
if (usi->data->ver == USI_VER2) {
usi->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(usi->regs))
return PTR_ERR(usi->regs);
}
ret = exynos_usi_configure(usi);
if (ret)
return ret;
/* Make it possible to embed protocol nodes into USI np */
return of_platform_populate(np, NULL, NULL, dev);
}
static int __maybe_unused exynos_usi_resume_noirq(struct device *dev)
{
struct exynos_usi *usi = dev_get_drvdata(dev);
return exynos_usi_configure(usi);
}
static const struct dev_pm_ops exynos_usi_pm = {
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(NULL, exynos_usi_resume_noirq)
};
static struct platform_driver exynos_usi_driver = {
.driver = {
.name = "exynos-usi",
.pm = &exynos_usi_pm,
.of_match_table = exynos_usi_dt_match,
},
.probe = exynos_usi_probe,
};
module_platform_driver(exynos_usi_driver);
MODULE_DESCRIPTION("Samsung USI driver");
MODULE_AUTHOR("Sam Protsenko <semen.protsenko@linaro.org>");
MODULE_LICENSE("GPL");
......@@ -10,6 +10,7 @@
#include <linux/export.h>
#include <linux/of.h>
#include <linux/pm_opp.h>
#include <linux/pm_runtime.h>
#include <soc/tegra/common.h>
#include <soc/tegra/fuse.h>
......@@ -43,6 +44,7 @@ static int tegra_core_dev_init_opp_state(struct device *dev)
{
unsigned long rate;
struct clk *clk;
bool rpm_enabled;
int err;
clk = devm_clk_get(dev, NULL);
......@@ -57,8 +59,31 @@ static int tegra_core_dev_init_opp_state(struct device *dev)
return -EINVAL;
}
/*
* Runtime PM of the device must be enabled in order to set up
* GENPD's performance properly because GENPD core checks whether
* device is suspended and this check doesn't work while RPM is
* disabled. This makes sure the OPP vote below gets cached in
* GENPD for the device. Instead, the vote is done the next time
* the device gets runtime resumed.
*/
rpm_enabled = pm_runtime_enabled(dev);
if (!rpm_enabled)
pm_runtime_enable(dev);
/* should never happen in practice */
if (!pm_runtime_enabled(dev)) {
dev_WARN(dev, "failed to enable runtime PM\n");
pm_runtime_disable(dev);
return -EINVAL;
}
/* first dummy rate-setting initializes voltage vote */
err = dev_pm_opp_set_rate(dev, rate);
if (!rpm_enabled)
pm_runtime_disable(dev);
if (err) {
dev_err(dev, "failed to initialize OPP clock: %d\n", err);
return err;
......@@ -111,9 +136,7 @@ int devm_tegra_core_dev_init_opp_table(struct device *dev,
*/
err = devm_pm_opp_of_add_table(dev);
if (err) {
if (err == -ENODEV)
dev_err_once(dev, "OPP table not found, please update device-tree\n");
else
if (err != -ENODEV)
dev_err(dev, "failed to add OPP table: %d\n", err);
return err;
......
......@@ -14,6 +14,7 @@
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <linux/slab.h>
#include <linux/sys_soc.h>
......@@ -181,6 +182,12 @@ static const struct nvmem_cell_info tegra_fuse_cells[] = {
},
};
static void tegra_fuse_restore(void *base)
{
fuse->clk = NULL;
fuse->base = base;
}
static int tegra_fuse_probe(struct platform_device *pdev)
{
void __iomem *base = fuse->base;
......@@ -188,13 +195,16 @@ static int tegra_fuse_probe(struct platform_device *pdev)
struct resource *res;
int err;
err = devm_add_action(&pdev->dev, tegra_fuse_restore, base);
if (err)
return err;
/* take over the memory region from the early initialization */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
fuse->phys = res->start;
fuse->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(fuse->base)) {
err = PTR_ERR(fuse->base);
fuse->base = base;
return err;
}
......@@ -204,19 +214,20 @@ static int tegra_fuse_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "failed to get FUSE clock: %ld",
PTR_ERR(fuse->clk));
fuse->base = base;
return PTR_ERR(fuse->clk);
}
platform_set_drvdata(pdev, fuse);
fuse->dev = &pdev->dev;
pm_runtime_enable(&pdev->dev);
err = devm_pm_runtime_enable(&pdev->dev);
if (err)
return err;
if (fuse->soc->probe) {
err = fuse->soc->probe(fuse);
if (err < 0)
goto restore;
return err;
}
memset(&nvmem, 0, sizeof(nvmem));
......@@ -240,19 +251,37 @@ static int tegra_fuse_probe(struct platform_device *pdev)
err = PTR_ERR(fuse->nvmem);
dev_err(&pdev->dev, "failed to register NVMEM device: %d\n",
err);
goto restore;
return err;
}
fuse->rst = devm_reset_control_get_optional(&pdev->dev, "fuse");
if (IS_ERR(fuse->rst)) {
err = PTR_ERR(fuse->rst);
dev_err(&pdev->dev, "failed to get FUSE reset: %pe\n",
fuse->rst);
return err;
}
/*
* FUSE clock is enabled at a boot time, hence this resume/suspend
* disables the clock besides the h/w resetting.
*/
err = pm_runtime_resume_and_get(&pdev->dev);
if (err)
return err;
err = reset_control_reset(fuse->rst);
pm_runtime_put(&pdev->dev);
if (err < 0) {
dev_err(&pdev->dev, "failed to reset FUSE: %d\n", err);
return err;
}
/* release the early I/O memory mapping */
iounmap(base);
return 0;
restore:
fuse->clk = NULL;
fuse->base = base;
pm_runtime_disable(&pdev->dev);
return err;
}
static int __maybe_unused tegra_fuse_runtime_resume(struct device *dev)
......
......@@ -94,9 +94,28 @@ static bool dma_filter(struct dma_chan *chan, void *filter_param)
return of_device_is_compatible(np, "nvidia,tegra20-apbdma");
}
static void tegra20_fuse_release_channel(void *data)
{
struct tegra_fuse *fuse = data;
dma_release_channel(fuse->apbdma.chan);
fuse->apbdma.chan = NULL;
}
static void tegra20_fuse_free_coherent(void *data)
{
struct tegra_fuse *fuse = data;
dma_free_coherent(fuse->dev, sizeof(u32), fuse->apbdma.virt,
fuse->apbdma.phys);
fuse->apbdma.virt = NULL;
fuse->apbdma.phys = 0x0;
}
static int tegra20_fuse_probe(struct tegra_fuse *fuse)
{
dma_cap_mask_t mask;
int err;
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
......@@ -105,13 +124,21 @@ static int tegra20_fuse_probe(struct tegra_fuse *fuse)
if (!fuse->apbdma.chan)
return -EPROBE_DEFER;
err = devm_add_action_or_reset(fuse->dev, tegra20_fuse_release_channel,
fuse);
if (err)
return err;
fuse->apbdma.virt = dma_alloc_coherent(fuse->dev, sizeof(u32),
&fuse->apbdma.phys,
GFP_KERNEL);
if (!fuse->apbdma.virt) {
dma_release_channel(fuse->apbdma.chan);
if (!fuse->apbdma.virt)
return -ENOMEM;
}
err = devm_add_action_or_reset(fuse->dev, tegra20_fuse_free_coherent,
fuse);
if (err)
return err;
fuse->apbdma.config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
fuse->apbdma.config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
......
......@@ -43,6 +43,7 @@ struct tegra_fuse {
void __iomem *base;
phys_addr_t phys;
struct clk *clk;
struct reset_control *rst;
u32 (*read_early)(struct tegra_fuse *fuse, unsigned int offset);
u32 (*read)(struct tegra_fuse *fuse, unsigned int offset);
......
......@@ -1064,10 +1064,8 @@ int tegra_pmc_cpu_remove_clamping(unsigned int cpuid)
return tegra_powergate_remove_clamping(id);
}
static int tegra_pmc_restart_notify(struct notifier_block *this,
unsigned long action, void *data)
static void tegra_pmc_program_reboot_reason(const char *cmd)
{
const char *cmd = data;
u32 value;
value = tegra_pmc_scratch_readl(pmc, pmc->soc->regs->scratch0);
......@@ -1085,6 +1083,25 @@ static int tegra_pmc_restart_notify(struct notifier_block *this,
}
tegra_pmc_scratch_writel(pmc, value, pmc->soc->regs->scratch0);
}
static int tegra_pmc_reboot_notify(struct notifier_block *this,
unsigned long action, void *data)
{
if (action == SYS_RESTART)
tegra_pmc_program_reboot_reason(data);
return NOTIFY_DONE;
}
static struct notifier_block tegra_pmc_reboot_notifier = {
.notifier_call = tegra_pmc_reboot_notify,
};
static int tegra_pmc_restart_notify(struct notifier_block *this,
unsigned long action, void *data)
{
u32 value;
/* reset everything but PMC_SCRATCH0 and PMC_RST_STATUS */
value = tegra_pmc_readl(pmc, PMC_CNTRL);
......@@ -1353,7 +1370,7 @@ static int tegra_pmc_core_pd_add(struct tegra_pmc *pmc, struct device_node *np)
if (!genpd)
return -ENOMEM;
genpd->name = np->name;
genpd->name = "core";
genpd->set_performance_state = tegra_pmc_core_pd_set_performance_state;
genpd->opp_to_performance_state = tegra_pmc_core_pd_opp_to_performance_state;
......@@ -2890,6 +2907,14 @@ static int tegra_pmc_probe(struct platform_device *pdev)
goto cleanup_sysfs;
}
err = devm_register_reboot_notifier(&pdev->dev,
&tegra_pmc_reboot_notifier);
if (err) {
dev_err(&pdev->dev, "unable to register reboot notifier, %d\n",
err);
goto cleanup_debugfs;
}
err = register_restart_handler(&tegra_pmc_restart_handler);
if (err) {
dev_err(&pdev->dev, "unable to register restart handler, %d\n",
......@@ -2963,7 +2988,7 @@ static SIMPLE_DEV_PM_OPS(tegra_pmc_pm_ops, tegra_pmc_suspend, tegra_pmc_resume);
static const char * const tegra20_powergates[] = {
[TEGRA_POWERGATE_CPU] = "cpu",
[TEGRA_POWERGATE_3D] = "3d",
[TEGRA_POWERGATE_3D] = "td",
[TEGRA_POWERGATE_VENC] = "venc",
[TEGRA_POWERGATE_VDEC] = "vdec",
[TEGRA_POWERGATE_PCIE] = "pcie",
......@@ -3071,7 +3096,7 @@ static const struct tegra_pmc_soc tegra20_pmc_soc = {
static const char * const tegra30_powergates[] = {
[TEGRA_POWERGATE_CPU] = "cpu0",
[TEGRA_POWERGATE_3D] = "3d0",
[TEGRA_POWERGATE_3D] = "td",
[TEGRA_POWERGATE_VENC] = "venc",
[TEGRA_POWERGATE_VDEC] = "vdec",
[TEGRA_POWERGATE_PCIE] = "pcie",
......@@ -3083,7 +3108,7 @@ static const char * const tegra30_powergates[] = {
[TEGRA_POWERGATE_CPU2] = "cpu2",
[TEGRA_POWERGATE_CPU3] = "cpu3",
[TEGRA_POWERGATE_CELP] = "celp",
[TEGRA_POWERGATE_3D1] = "3d1",
[TEGRA_POWERGATE_3D1] = "td2",
};
static const u8 tegra30_cpu_powergates[] = {
......@@ -3132,7 +3157,7 @@ static const struct tegra_pmc_soc tegra30_pmc_soc = {
static const char * const tegra114_powergates[] = {
[TEGRA_POWERGATE_CPU] = "crail",
[TEGRA_POWERGATE_3D] = "3d",
[TEGRA_POWERGATE_3D] = "td",
[TEGRA_POWERGATE_VENC] = "venc",
[TEGRA_POWERGATE_VDEC] = "vdec",
[TEGRA_POWERGATE_MPE] = "mpe",
......
......@@ -16,7 +16,9 @@
#include <linux/regulator/coupler.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/suspend.h>
#include <soc/tegra/fuse.h>
#include <soc/tegra/pmc.h>
struct tegra_regulator_coupler {
......@@ -25,9 +27,12 @@ struct tegra_regulator_coupler {
struct regulator_dev *cpu_rdev;
struct regulator_dev *rtc_rdev;
struct notifier_block reboot_notifier;
struct notifier_block suspend_notifier;
int core_min_uV, cpu_min_uV;
bool sys_reboot_mode_req;
bool sys_reboot_mode;
bool sys_suspend_mode_req;
bool sys_suspend_mode;
};
static inline struct tegra_regulator_coupler *
......@@ -105,6 +110,28 @@ static int tegra20_core_rtc_max_spread(struct regulator_dev *core_rdev,
return 150000;
}
static int tegra20_cpu_nominal_uV(void)
{
switch (tegra_sku_info.soc_speedo_id) {
case 0:
return 1100000;
case 1:
return 1025000;
default:
return 1125000;
}
}
static int tegra20_core_nominal_uV(void)
{
switch (tegra_sku_info.soc_speedo_id) {
default:
return 1225000;
case 2:
return 1300000;
}
}
static int tegra20_core_rtc_update(struct tegra_regulator_coupler *tegra,
struct regulator_dev *core_rdev,
struct regulator_dev *rtc_rdev,
......@@ -144,6 +171,11 @@ static int tegra20_core_rtc_update(struct tegra_regulator_coupler *tegra,
if (err)
return err;
/* prepare voltage level for suspend */
if (tegra->sys_suspend_mode)
core_min_uV = clamp(tegra20_core_nominal_uV(),
core_min_uV, core_max_uV);
core_uV = regulator_get_voltage_rdev(core_rdev);
if (core_uV < 0)
return core_uV;
......@@ -279,6 +311,11 @@ static int tegra20_cpu_voltage_update(struct tegra_regulator_coupler *tegra,
if (tegra->sys_reboot_mode)
cpu_min_uV = max(cpu_min_uV, tegra->cpu_min_uV);
/* prepare voltage level for suspend */
if (tegra->sys_suspend_mode)
cpu_min_uV = clamp(tegra20_cpu_nominal_uV(),
cpu_min_uV, cpu_max_uV);
if (cpu_min_uV > cpu_uV) {
err = tegra20_core_rtc_update(tegra, core_rdev, rtc_rdev,
cpu_uV, cpu_min_uV);
......@@ -320,6 +357,7 @@ static int tegra20_regulator_balance_voltage(struct regulator_coupler *coupler,
}
tegra->sys_reboot_mode = READ_ONCE(tegra->sys_reboot_mode_req);
tegra->sys_suspend_mode = READ_ONCE(tegra->sys_suspend_mode_req);
if (rdev == cpu_rdev)
return tegra20_cpu_voltage_update(tegra, cpu_rdev,
......@@ -334,6 +372,63 @@ static int tegra20_regulator_balance_voltage(struct regulator_coupler *coupler,
return -EPERM;
}
static int tegra20_regulator_prepare_suspend(struct tegra_regulator_coupler *tegra,
bool sys_suspend_mode)
{
int err;
if (!tegra->core_rdev || !tegra->rtc_rdev || !tegra->cpu_rdev)
return 0;
/*
* All power domains are enabled early during resume from suspend
* by GENPD core. Domains like VENC may require a higher voltage
* when enabled during resume from suspend. This also prepares
* hardware for resuming from LP0.
*/
WRITE_ONCE(tegra->sys_suspend_mode_req, sys_suspend_mode);
err = regulator_sync_voltage_rdev(tegra->cpu_rdev);
if (err)
return err;
err = regulator_sync_voltage_rdev(tegra->core_rdev);
if (err)
return err;
return 0;
}
static int tegra20_regulator_suspend(struct notifier_block *notifier,
unsigned long mode, void *arg)
{
struct tegra_regulator_coupler *tegra;
int ret = 0;
tegra = container_of(notifier, struct tegra_regulator_coupler,
suspend_notifier);
switch (mode) {
case PM_HIBERNATION_PREPARE:
case PM_RESTORE_PREPARE:
case PM_SUSPEND_PREPARE:
ret = tegra20_regulator_prepare_suspend(tegra, true);
break;
case PM_POST_HIBERNATION:
case PM_POST_RESTORE:
case PM_POST_SUSPEND:
ret = tegra20_regulator_prepare_suspend(tegra, false);
break;
}
if (ret)
pr_err("failed to prepare regulators: %d\n", ret);
return notifier_from_errno(ret);
}
static int tegra20_regulator_prepare_reboot(struct tegra_regulator_coupler *tegra,
bool sys_reboot_mode)
{
......@@ -444,6 +539,7 @@ static struct tegra_regulator_coupler tegra20_coupler = {
.balance_voltage = tegra20_regulator_balance_voltage,
},
.reboot_notifier.notifier_call = tegra20_regulator_reboot,
.suspend_notifier.notifier_call = tegra20_regulator_suspend,
};
static int __init tegra_regulator_coupler_init(void)
......@@ -456,6 +552,9 @@ static int __init tegra_regulator_coupler_init(void)
err = register_reboot_notifier(&tegra20_coupler.reboot_notifier);
WARN_ON(err);
err = register_pm_notifier(&tegra20_coupler.suspend_notifier);
WARN_ON(err);
return regulator_coupler_register(&tegra20_coupler.coupler);
}
arch_initcall(tegra_regulator_coupler_init);
......@@ -16,6 +16,7 @@
#include <linux/regulator/coupler.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/suspend.h>
#include <soc/tegra/fuse.h>
#include <soc/tegra/pmc.h>
......@@ -25,9 +26,12 @@ struct tegra_regulator_coupler {
struct regulator_dev *core_rdev;
struct regulator_dev *cpu_rdev;
struct notifier_block reboot_notifier;
struct notifier_block suspend_notifier;
int core_min_uV, cpu_min_uV;
bool sys_reboot_mode_req;
bool sys_reboot_mode;
bool sys_suspend_mode_req;
bool sys_suspend_mode;
};
static inline struct tegra_regulator_coupler *
......@@ -113,6 +117,52 @@ static int tegra30_core_cpu_limit(int cpu_uV)
return -EINVAL;
}
static int tegra30_cpu_nominal_uV(void)
{
switch (tegra_sku_info.cpu_speedo_id) {
case 10 ... 11:
return 850000;
case 9:
return 912000;
case 1 ... 3:
case 7 ... 8:
return 1050000;
default:
return 1125000;
case 4 ... 6:
case 12 ... 13:
return 1237000;
}
}
static int tegra30_core_nominal_uV(void)
{
switch (tegra_sku_info.soc_speedo_id) {
case 0:
return 1200000;
case 1:
if (tegra_sku_info.cpu_speedo_id != 7 &&
tegra_sku_info.cpu_speedo_id != 8)
return 1200000;
fallthrough;
case 2:
if (tegra_sku_info.cpu_speedo_id != 13)
return 1300000;
return 1350000;
default:
return 1250000;
}
}
static int tegra30_voltage_update(struct tegra_regulator_coupler *tegra,
struct regulator_dev *cpu_rdev,
struct regulator_dev *core_rdev)
......@@ -168,6 +218,11 @@ static int tegra30_voltage_update(struct tegra_regulator_coupler *tegra,
if (err)
return err;
/* prepare voltage level for suspend */
if (tegra->sys_suspend_mode)
core_min_uV = clamp(tegra30_core_nominal_uV(),
core_min_uV, core_max_uV);
core_uV = regulator_get_voltage_rdev(core_rdev);
if (core_uV < 0)
return core_uV;
......@@ -223,6 +278,11 @@ static int tegra30_voltage_update(struct tegra_regulator_coupler *tegra,
if (tegra->sys_reboot_mode)
cpu_min_uV = max(cpu_min_uV, tegra->cpu_min_uV);
/* prepare voltage level for suspend */
if (tegra->sys_suspend_mode)
cpu_min_uV = clamp(tegra30_cpu_nominal_uV(),
cpu_min_uV, cpu_max_uV);
if (core_min_limited_uV > core_uV) {
pr_err("core voltage constraint violated: %d %d %d\n",
core_uV, core_min_limited_uV, cpu_uV);
......@@ -292,10 +352,68 @@ static int tegra30_regulator_balance_voltage(struct regulator_coupler *coupler,
}
tegra->sys_reboot_mode = READ_ONCE(tegra->sys_reboot_mode_req);
tegra->sys_suspend_mode = READ_ONCE(tegra->sys_suspend_mode_req);
return tegra30_voltage_update(tegra, cpu_rdev, core_rdev);
}
static int tegra30_regulator_prepare_suspend(struct tegra_regulator_coupler *tegra,
bool sys_suspend_mode)
{
int err;
if (!tegra->core_rdev || !tegra->cpu_rdev)
return 0;
/*
* All power domains are enabled early during resume from suspend
* by GENPD core. Domains like VENC may require a higher voltage
* when enabled during resume from suspend. This also prepares
* hardware for resuming from LP0.
*/
WRITE_ONCE(tegra->sys_suspend_mode_req, sys_suspend_mode);
err = regulator_sync_voltage_rdev(tegra->cpu_rdev);
if (err)
return err;
err = regulator_sync_voltage_rdev(tegra->core_rdev);
if (err)
return err;
return 0;
}
static int tegra30_regulator_suspend(struct notifier_block *notifier,
unsigned long mode, void *arg)
{
struct tegra_regulator_coupler *tegra;
int ret = 0;
tegra = container_of(notifier, struct tegra_regulator_coupler,
suspend_notifier);
switch (mode) {
case PM_HIBERNATION_PREPARE:
case PM_RESTORE_PREPARE:
case PM_SUSPEND_PREPARE:
ret = tegra30_regulator_prepare_suspend(tegra, true);
break;
case PM_POST_HIBERNATION:
case PM_POST_RESTORE:
case PM_POST_SUSPEND:
ret = tegra30_regulator_prepare_suspend(tegra, false);
break;
}
if (ret)
pr_err("failed to prepare regulators: %d\n", ret);
return notifier_from_errno(ret);
}
static int tegra30_regulator_prepare_reboot(struct tegra_regulator_coupler *tegra,
bool sys_reboot_mode)
{
......@@ -395,6 +513,7 @@ static struct tegra_regulator_coupler tegra30_coupler = {
.balance_voltage = tegra30_regulator_balance_voltage,
},
.reboot_notifier.notifier_call = tegra30_regulator_reboot,
.suspend_notifier.notifier_call = tegra30_regulator_suspend,
};
static int __init tegra_regulator_coupler_init(void)
......@@ -407,6 +526,9 @@ static int __init tegra_regulator_coupler_init(void)
err = register_reboot_notifier(&tegra30_coupler.reboot_notifier);
WARN_ON(err);
err = register_pm_notifier(&tegra30_coupler.suspend_notifier);
WARN_ON(err);
return regulator_coupler_register(&tegra30_coupler.coupler);
}
arch_initcall(tegra_regulator_coupler_init);
......@@ -40,7 +40,8 @@ static const struct k3_soc_id {
{ 0xBB5A, "AM65X" },
{ 0xBB64, "J721E" },
{ 0xBB6D, "J7200" },
{ 0xBB38, "AM64X" }
{ 0xBB38, "AM64X" },
{ 0xBB75, "J721S2"},
};
static int
......
......@@ -646,31 +646,31 @@ static int dma_init(struct device_node *cloud, struct device_node *dma_node)
}
dma->reg_global = pktdma_get_regs(dma, node, 0, &size);
if (!dma->reg_global)
return -ENODEV;
if (IS_ERR(dma->reg_global))
return PTR_ERR(dma->reg_global);
if (size < sizeof(struct reg_global)) {
dev_err(kdev->dev, "bad size %pa for global regs\n", &size);
return -ENODEV;
}
dma->reg_tx_chan = pktdma_get_regs(dma, node, 1, &size);
if (!dma->reg_tx_chan)
return -ENODEV;
if (IS_ERR(dma->reg_tx_chan))
return PTR_ERR(dma->reg_tx_chan);
max_tx_chan = size / sizeof(struct reg_chan);
dma->reg_rx_chan = pktdma_get_regs(dma, node, 2, &size);
if (!dma->reg_rx_chan)
return -ENODEV;
if (IS_ERR(dma->reg_rx_chan))
return PTR_ERR(dma->reg_rx_chan);
max_rx_chan = size / sizeof(struct reg_chan);
dma->reg_tx_sched = pktdma_get_regs(dma, node, 3, &size);
if (!dma->reg_tx_sched)
return -ENODEV;
if (IS_ERR(dma->reg_tx_sched))
return PTR_ERR(dma->reg_tx_sched);
max_tx_sched = size / sizeof(struct reg_tx_sched);
dma->reg_rx_flow = pktdma_get_regs(dma, node, 4, &size);
if (!dma->reg_rx_flow)
return -ENODEV;
if (IS_ERR(dma->reg_rx_flow))
return PTR_ERR(dma->reg_rx_flow);
max_rx_flow = size / sizeof(struct reg_rx_flow);
dma->rx_priority = DMA_PRIO_DEFAULT;
......
......@@ -129,7 +129,7 @@ static int pruss_clk_init(struct pruss *pruss, struct device_node *cfg_node)
clks_np = of_get_child_by_name(cfg_node, "clocks");
if (!clks_np) {
dev_err(dev, "%pOF is missing its 'clocks' node\n", clks_np);
dev_err(dev, "%pOF is missing its 'clocks' node\n", cfg_node);
return -ENODEV;
}
......
......@@ -20,8 +20,6 @@
#include <linux/firmware/xlnx-zynqmp.h>
#define ZYNQMP_NUM_DOMAINS (100)
/* Flag stating if PM nodes mapped to the PM domain has been requested */
#define ZYNQMP_PM_DOMAIN_REQUESTED BIT(0)
static int min_capability;
......@@ -29,14 +27,17 @@ static int min_capability;
* struct zynqmp_pm_domain - Wrapper around struct generic_pm_domain
* @gpd: Generic power domain
* @node_id: PM node ID corresponding to device inside PM domain
* @flags: ZynqMP PM domain flags
* @requested: The PM node mapped to the PM domain has been requested
*/
struct zynqmp_pm_domain {
struct generic_pm_domain gpd;
u32 node_id;
u8 flags;
bool requested;
};
#define to_zynqmp_pm_domain(pm_domain) \
container_of(pm_domain, struct zynqmp_pm_domain, gpd)
/**
* zynqmp_gpd_is_active_wakeup_path() - Check if device is in wakeup source
* path
......@@ -71,21 +72,23 @@ static int zynqmp_gpd_is_active_wakeup_path(struct device *dev, void *not_used)
*/
static int zynqmp_gpd_power_on(struct generic_pm_domain *domain)
{
struct zynqmp_pm_domain *pd = to_zynqmp_pm_domain(domain);
int ret;
struct zynqmp_pm_domain *pd;
pd = container_of(domain, struct zynqmp_pm_domain, gpd);
ret = zynqmp_pm_set_requirement(pd->node_id,
ZYNQMP_PM_CAPABILITY_ACCESS,
ZYNQMP_PM_MAX_QOS,
ZYNQMP_PM_REQUEST_ACK_BLOCKING);
if (ret) {
pr_err("%s() %s set requirement for node %d failed: %d\n",
__func__, domain->name, pd->node_id, ret);
dev_err(&domain->dev,
"failed to set requirement to 0x%x for PM node id %d: %d\n",
ZYNQMP_PM_CAPABILITY_ACCESS, pd->node_id, ret);
return ret;
}
pr_debug("%s() Powered on %s domain\n", __func__, domain->name);
dev_dbg(&domain->dev, "set requirement to 0x%x for PM node id %d\n",
ZYNQMP_PM_CAPABILITY_ACCESS, pd->node_id);
return 0;
}
......@@ -100,18 +103,16 @@ static int zynqmp_gpd_power_on(struct generic_pm_domain *domain)
*/
static int zynqmp_gpd_power_off(struct generic_pm_domain *domain)
{
struct zynqmp_pm_domain *pd = to_zynqmp_pm_domain(domain);
int ret;
struct pm_domain_data *pdd, *tmp;
struct zynqmp_pm_domain *pd;
u32 capabilities = min_capability;
bool may_wakeup;
pd = container_of(domain, struct zynqmp_pm_domain, gpd);
/* If domain is already released there is nothing to be done */
if (!(pd->flags & ZYNQMP_PM_DOMAIN_REQUESTED)) {
pr_debug("%s() %s domain is already released\n",
__func__, domain->name);
if (!pd->requested) {
dev_dbg(&domain->dev, "PM node id %d is already released\n",
pd->node_id);
return 0;
}
......@@ -128,17 +129,16 @@ static int zynqmp_gpd_power_off(struct generic_pm_domain *domain)
ret = zynqmp_pm_set_requirement(pd->node_id, capabilities, 0,
ZYNQMP_PM_REQUEST_ACK_NO);
/**
* If powering down of any node inside this domain fails,
* report and return the error
*/
if (ret) {
pr_err("%s() %s set requirement for node %d failed: %d\n",
__func__, domain->name, pd->node_id, ret);
dev_err(&domain->dev,
"failed to set requirement to 0x%x for PM node id %d: %d\n",
capabilities, pd->node_id, ret);
return ret;
}
pr_debug("%s() Powered off %s domain\n", __func__, domain->name);
dev_dbg(&domain->dev, "set requirement to 0x%x for PM node id %d\n",
capabilities, pd->node_id);
return 0;
}
......@@ -152,10 +152,14 @@ static int zynqmp_gpd_power_off(struct generic_pm_domain *domain)
static int zynqmp_gpd_attach_dev(struct generic_pm_domain *domain,
struct device *dev)
{
struct zynqmp_pm_domain *pd = to_zynqmp_pm_domain(domain);
struct device_link *link;
int ret;
struct zynqmp_pm_domain *pd;
pd = container_of(domain, struct zynqmp_pm_domain, gpd);
link = device_link_add(dev, &domain->dev, DL_FLAG_SYNC_STATE_ONLY);
if (!link)
dev_dbg(&domain->dev, "failed to create device link for %s\n",
dev_name(dev));
/* If this is not the first device to attach there is nothing to do */
if (domain->device_count)
......@@ -163,17 +167,17 @@ static int zynqmp_gpd_attach_dev(struct generic_pm_domain *domain,
ret = zynqmp_pm_request_node(pd->node_id, 0, 0,
ZYNQMP_PM_REQUEST_ACK_BLOCKING);
/* If requesting a node fails print and return the error */
if (ret) {
pr_err("%s() %s request failed for node %d: %d\n",
__func__, domain->name, pd->node_id, ret);
dev_err(&domain->dev, "%s request failed for node %d: %d\n",
domain->name, pd->node_id, ret);
return ret;
}
pd->flags |= ZYNQMP_PM_DOMAIN_REQUESTED;
pd->requested = true;
dev_dbg(&domain->dev, "%s requested PM node id %d\n",
dev_name(dev), pd->node_id);
pr_debug("%s() %s attached to %s domain\n", __func__,
dev_name(dev), domain->name);
return 0;
}
......@@ -185,27 +189,24 @@ static int zynqmp_gpd_attach_dev(struct generic_pm_domain *domain,
static void zynqmp_gpd_detach_dev(struct generic_pm_domain *domain,
struct device *dev)
{
struct zynqmp_pm_domain *pd = to_zynqmp_pm_domain(domain);
int ret;
struct zynqmp_pm_domain *pd;
pd = container_of(domain, struct zynqmp_pm_domain, gpd);
/* If this is not the last device to detach there is nothing to do */
if (domain->device_count)
return;
ret = zynqmp_pm_release_node(pd->node_id);
/* If releasing a node fails print the error and return */
if (ret) {
pr_err("%s() %s release failed for node %d: %d\n",
__func__, domain->name, pd->node_id, ret);
dev_err(&domain->dev, "failed to release PM node id %d: %d\n",
pd->node_id, ret);
return;
}
pd->flags &= ~ZYNQMP_PM_DOMAIN_REQUESTED;
pd->requested = false;
pr_debug("%s() %s detached from %s domain\n", __func__,
dev_name(dev), domain->name);
dev_dbg(&domain->dev, "%s released PM node id %d\n",
dev_name(dev), pd->node_id);
}
static struct generic_pm_domain *zynqmp_gpd_xlate
......@@ -215,7 +216,7 @@ static struct generic_pm_domain *zynqmp_gpd_xlate
unsigned int i, idx = genpdspec->args[0];
struct zynqmp_pm_domain *pd;
pd = container_of(genpd_data->domains[0], struct zynqmp_pm_domain, gpd);
pd = to_zynqmp_pm_domain(genpd_data->domains[0]);
if (genpdspec->args_count != 1)
return ERR_PTR(-EINVAL);
......@@ -299,9 +300,19 @@ static int zynqmp_gpd_remove(struct platform_device *pdev)
return 0;
}
static void zynqmp_gpd_sync_state(struct device *dev)
{
int ret;
ret = zynqmp_pm_init_finalize();
if (ret)
dev_warn(dev, "failed to release power management to firmware\n");
}
static struct platform_driver zynqmp_power_domain_driver = {
.driver = {
.name = "zynqmp_power_controller",
.sync_state = zynqmp_gpd_sync_state,
},
.probe = zynqmp_gpd_probe,
.remove = zynqmp_gpd_remove,
......
......@@ -178,7 +178,6 @@ static int zynqmp_pm_probe(struct platform_device *pdev)
u32 pm_api_version;
struct mbox_client *client;
zynqmp_pm_init_finalize();
zynqmp_pm_get_api_version(&pm_api_version);
/* Check PM API version number */
......
......@@ -156,7 +156,9 @@ static int rpcif_spi_probe(struct platform_device *pdev)
ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_TX_QUAD | SPI_RX_QUAD;
ctlr->flags = SPI_CONTROLLER_HALF_DUPLEX;
rpcif_hw_init(rpc, false);
error = rpcif_hw_init(rpc, false);
if (error)
return error;
error = spi_register_controller(ctlr);
if (error) {
......
......@@ -18,12 +18,15 @@
#include <linux/kthread.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_opp.h>
#include <linux/pm_runtime.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/reset.h>
#include <linux/spi/spi.h>
#include <soc/tegra/common.h>
#define SLINK_COMMAND 0x000
#define SLINK_BIT_LENGTH(x) (((x) & 0x1f) << 0)
#define SLINK_WORD_SIZE(x) (((x) & 0x1f) << 5)
......@@ -680,7 +683,7 @@ static int tegra_slink_start_transfer_one(struct spi_device *spi,
bits_per_word = t->bits_per_word;
speed = t->speed_hz;
if (speed != tspi->cur_speed) {
clk_set_rate(tspi->clk, speed * 4);
dev_pm_opp_set_rate(tspi->dev, speed * 4);
tspi->cur_speed = speed;
}
......@@ -1066,6 +1069,10 @@ static int tegra_slink_probe(struct platform_device *pdev)
goto exit_free_master;
}
ret = devm_tegra_core_dev_init_opp_table_common(&pdev->dev);
if (ret)
goto exit_free_master;
tspi->max_buf_size = SLINK_FIFO_DEPTH << 2;
tspi->dma_buf_size = DEFAULT_SPI_DMA_BUF_LEN;
......
......@@ -20,6 +20,7 @@
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <soc/tegra/common.h>
#include <soc/tegra/pmc.h>
#include "uapi.h"
......@@ -920,13 +921,17 @@ static __maybe_unused int tegra_vde_runtime_suspend(struct device *dev)
struct tegra_vde *vde = dev_get_drvdata(dev);
int err;
err = tegra_powergate_power_off(TEGRA_POWERGATE_VDEC);
if (err) {
dev_err(dev, "Failed to power down HW: %d\n", err);
return err;
if (!dev->pm_domain) {
err = tegra_powergate_power_off(TEGRA_POWERGATE_VDEC);
if (err) {
dev_err(dev, "Failed to power down HW: %d\n", err);
return err;
}
}
clk_disable_unprepare(vde->clk);
reset_control_release(vde->rst);
reset_control_release(vde->rst_mc);
return 0;
}
......@@ -936,14 +941,45 @@ static __maybe_unused int tegra_vde_runtime_resume(struct device *dev)
struct tegra_vde *vde = dev_get_drvdata(dev);
int err;
err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_VDEC,
vde->clk, vde->rst);
err = reset_control_acquire(vde->rst_mc);
if (err) {
dev_err(dev, "Failed to power up HW : %d\n", err);
dev_err(dev, "Failed to acquire mc reset: %d\n", err);
return err;
}
err = reset_control_acquire(vde->rst);
if (err) {
dev_err(dev, "Failed to acquire reset: %d\n", err);
goto release_mc_reset;
}
if (!dev->pm_domain) {
err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_VDEC,
vde->clk, vde->rst);
if (err) {
dev_err(dev, "Failed to power up HW : %d\n", err);
goto release_reset;
}
} else {
/*
* tegra_powergate_sequence_power_up() leaves clocks enabled,
* while GENPD not.
*/
err = clk_prepare_enable(vde->clk);
if (err) {
dev_err(dev, "Failed to enable clock: %d\n", err);
goto release_reset;
}
}
return 0;
release_reset:
reset_control_release(vde->rst);
release_mc_reset:
reset_control_release(vde->rst_mc);
return err;
}
static int tegra_vde_probe(struct platform_device *pdev)
......@@ -1001,14 +1037,14 @@ static int tegra_vde_probe(struct platform_device *pdev)
return err;
}
vde->rst = devm_reset_control_get(dev, NULL);
vde->rst = devm_reset_control_get_exclusive_released(dev, NULL);
if (IS_ERR(vde->rst)) {
err = PTR_ERR(vde->rst);
dev_err(dev, "Could not get VDE reset %d\n", err);
return err;
}
vde->rst_mc = devm_reset_control_get_optional(dev, "mc");
vde->rst_mc = devm_reset_control_get_optional_exclusive_released(dev, "mc");
if (IS_ERR(vde->rst_mc)) {
err = PTR_ERR(vde->rst_mc);
dev_err(dev, "Could not get MC reset %d\n", err);
......@@ -1026,6 +1062,12 @@ static int tegra_vde_probe(struct platform_device *pdev)
return err;
}
err = devm_tegra_core_dev_init_opp_table_common(dev);
if (err) {
dev_err(dev, "Could initialize OPP table %d\n", err);
return err;
}
vde->iram_pool = of_gen_pool_get(dev->of_node, "iram", 0);
if (!vde->iram_pool) {
dev_err(dev, "Could not get IRAM pool\n");
......@@ -1133,8 +1175,7 @@ static void tegra_vde_shutdown(struct platform_device *pdev)
* On some devices bootloader isn't ready to a power-gated VDE on
* a warm-reboot, machine will hang in that case.
*/
if (pm_runtime_status_suspended(&pdev->dev))
tegra_vde_runtime_resume(&pdev->dev);
pm_runtime_get_sync(&pdev->dev);
}
static __maybe_unused int tegra_vde_pm_suspend(struct device *dev)
......
......@@ -2,6 +2,7 @@
obj-$(CONFIG_OPTEE) += optee.o
optee-objs += core.o
optee-objs += call.o
optee-objs += notif.o
optee-objs += rpc.o
optee-objs += supp.o
optee-objs += device.o
......
......@@ -157,6 +157,7 @@ void optee_remove_common(struct optee *optee)
/* Unregister OP-TEE specific client devices on TEE bus */
optee_unregister_devices();
optee_notif_uninit(optee);
/*
* The two devices have to be unregistered before we can free the
* other resources.
......@@ -165,7 +166,6 @@ void optee_remove_common(struct optee *optee)
tee_device_unregister(optee->teedev);
tee_shm_pool_free(optee->pool);
optee_wait_queue_exit(&optee->wait_queue);
optee_supp_uninit(&optee->supp);
mutex_destroy(&optee->call_queue.mutex);
}
......
......@@ -855,9 +855,13 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
mutex_init(&optee->ffa.mutex);
mutex_init(&optee->call_queue.mutex);
INIT_LIST_HEAD(&optee->call_queue.waiters);
optee_wait_queue_init(&optee->wait_queue);
optee_supp_init(&optee->supp);
ffa_dev_set_drvdata(ffa_dev, optee);
rc = optee_notif_init(optee, OPTEE_DEFAULT_MAX_NOTIF_VALUE);
if (rc) {
optee_ffa_remove(ffa_dev);
return rc;
}
rc = optee_enumerate_devices(PTA_CMD_GET_DEVICES);
if (rc) {
......
This diff is collapsed.
......@@ -318,6 +318,13 @@ struct optee_msg_arg {
* [in] param[0].u.rmem.shm_ref holds shared memory reference
* [in] param[0].u.rmem.offs 0
* [in] param[0].u.rmem.size 0
*
* OPTEE_MSG_CMD_DO_BOTTOM_HALF does the scheduled bottom half processing
* of a driver.
*
* OPTEE_MSG_CMD_STOP_ASYNC_NOTIF informs secure world that from now is
* normal world unable to process asynchronous notifications. Typically
* used when the driver is shut down.
*/
#define OPTEE_MSG_CMD_OPEN_SESSION 0
#define OPTEE_MSG_CMD_INVOKE_COMMAND 1
......@@ -325,6 +332,8 @@ struct optee_msg_arg {
#define OPTEE_MSG_CMD_CANCEL 3
#define OPTEE_MSG_CMD_REGISTER_SHM 4
#define OPTEE_MSG_CMD_UNREGISTER_SHM 5
#define OPTEE_MSG_CMD_DO_BOTTOM_HALF 6
#define OPTEE_MSG_CMD_STOP_ASYNC_NOTIF 7
#define OPTEE_MSG_FUNCID_CALL_WITH_ARG 0x0004
#endif /* _OPTEE_MSG_H */
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -12,4 +12,9 @@
#define IMX8MN_POWER_DOMAIN_DISPMIX 3
#define IMX8MN_POWER_DOMAIN_MIPI 4
#define IMX8MN_DISPBLK_PD_MIPI_DSI 0
#define IMX8MN_DISPBLK_PD_MIPI_CSI 1
#define IMX8MN_DISPBLK_PD_LCDIF 2
#define IMX8MN_DISPBLK_PD_ISI 3
#endif
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment