aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/drivers/soc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/soc')
-rw-r--r--drivers/soc/amlogic/meson-canvas.c4
-rw-r--r--drivers/soc/amlogic/meson-clk-measure.c4
-rw-r--r--drivers/soc/amlogic/meson-gx-socinfo.c1
-rw-r--r--drivers/soc/aspeed/Kconfig10
-rw-r--r--drivers/soc/aspeed/Makefile9
-rw-r--r--drivers/soc/aspeed/aspeed-uart-routing.c603
-rw-r--r--drivers/soc/bcm/bcm63xx/bcm-pmb.c4
-rw-r--r--drivers/soc/bcm/bcm63xx/bcm63xx-power.c4
-rw-r--r--drivers/soc/bcm/brcmstb/biuctrl.c2
-rw-r--r--drivers/soc/fsl/Kconfig1
-rw-r--r--drivers/soc/fsl/dpio/dpio-cmd.h3
-rw-r--r--drivers/soc/fsl/dpio/dpio-driver.c1
-rw-r--r--drivers/soc/fsl/dpio/dpio-service.c119
-rw-r--r--drivers/soc/fsl/dpio/dpio.c1
-rw-r--r--drivers/soc/fsl/dpio/dpio.h2
-rw-r--r--drivers/soc/fsl/dpio/qbman-portal.c66
-rw-r--r--drivers/soc/fsl/dpio/qbman-portal.h13
-rw-r--r--drivers/soc/fsl/guts.c4
-rw-r--r--drivers/soc/fsl/rcpm.c7
-rw-r--r--drivers/soc/imx/Kconfig1
-rw-r--r--drivers/soc/imx/Makefile1
-rw-r--r--drivers/soc/imx/gpcv2.c134
-rw-r--r--drivers/soc/imx/imx8m-blk-ctrl.c523
-rw-r--r--drivers/soc/mediatek/mt8192-mmsys.h76
-rw-r--r--drivers/soc/mediatek/mtk-mmsys.c79
-rw-r--r--drivers/soc/mediatek/mtk-mmsys.h2
-rw-r--r--drivers/soc/mediatek/mtk-mutex.c35
-rw-r--r--drivers/soc/qcom/Kconfig21
-rw-r--r--drivers/soc/qcom/Makefile2
-rw-r--r--drivers/soc/qcom/apr.c289
-rw-r--r--drivers/soc/qcom/cpr.c4
-rw-r--r--drivers/soc/qcom/llcc-qcom.c18
-rw-r--r--drivers/soc/qcom/ocmem.c4
-rw-r--r--drivers/soc/qcom/pdr_interface.c12
-rw-r--r--drivers/soc/qcom/qcom-geni-se.c4
-rw-r--r--drivers/soc/qcom/qcom_aoss.c165
-rw-r--r--drivers/soc/qcom/qcom_gsbi.c4
-rw-r--r--drivers/soc/qcom/qcom_stats.c277
-rw-r--r--drivers/soc/qcom/rpmh-rsc.c4
-rw-r--r--drivers/soc/qcom/rpmhpd.c36
-rw-r--r--drivers/soc/qcom/rpmpd.c24
-rw-r--r--drivers/soc/qcom/smd-rpm.c2
-rw-r--r--drivers/soc/qcom/smem.c57
-rw-r--r--drivers/soc/qcom/smp2p.c154
-rw-r--r--drivers/soc/qcom/socinfo.c18
-rw-r--r--drivers/soc/qcom/spm.c279
-rw-r--r--drivers/soc/renesas/Kconfig7
-rw-r--r--drivers/soc/renesas/renesas-soc.c7
-rw-r--r--drivers/soc/samsung/Kconfig5
-rw-r--r--drivers/soc/samsung/Makefile3
-rw-r--r--drivers/soc/samsung/exynos-chipid.c94
-rw-r--r--drivers/soc/samsung/exynos5422-asv.c1
-rw-r--r--drivers/soc/samsung/pm_domains.c1
-rw-r--r--drivers/soc/sunxi/sunxi_sram.c4
-rw-r--r--drivers/soc/tegra/Makefile1
-rw-r--r--drivers/soc/tegra/ari-tegra186.c80
-rw-r--r--drivers/soc/tegra/pmc.c28
57 files changed, 2976 insertions, 338 deletions
diff --git a/drivers/soc/amlogic/meson-canvas.c b/drivers/soc/amlogic/meson-canvas.c
index d0329ad170d1..383b0cfc584e 100644
--- a/drivers/soc/amlogic/meson-canvas.c
+++ b/drivers/soc/amlogic/meson-canvas.c
@@ -168,7 +168,6 @@ EXPORT_SYMBOL_GPL(meson_canvas_free);
static int meson_canvas_probe(struct platform_device *pdev)
{
- struct resource *res;
struct meson_canvas *canvas;
struct device *dev = &pdev->dev;
@@ -176,8 +175,7 @@ static int meson_canvas_probe(struct platform_device *pdev)
if (!canvas)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- canvas->reg_base = devm_ioremap_resource(dev, res);
+ canvas->reg_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(canvas->reg_base))
return PTR_ERR(canvas->reg_base);
diff --git a/drivers/soc/amlogic/meson-clk-measure.c b/drivers/soc/amlogic/meson-clk-measure.c
index 6dd190270123..3f3039600357 100644
--- a/drivers/soc/amlogic/meson-clk-measure.c
+++ b/drivers/soc/amlogic/meson-clk-measure.c
@@ -606,7 +606,6 @@ static int meson_msr_probe(struct platform_device *pdev)
{
const struct meson_msr_id *match_data;
struct meson_msr *priv;
- struct resource *res;
struct dentry *root, *clks;
void __iomem *base;
int i;
@@ -624,8 +623,7 @@ static int meson_msr_probe(struct platform_device *pdev)
memcpy(priv->msr_table, match_data, sizeof(priv->msr_table));
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(&pdev->dev, res);
+ base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
diff --git a/drivers/soc/amlogic/meson-gx-socinfo.c b/drivers/soc/amlogic/meson-gx-socinfo.c
index 6f54bd832c8b..165f7548401b 100644
--- a/drivers/soc/amlogic/meson-gx-socinfo.c
+++ b/drivers/soc/amlogic/meson-gx-socinfo.c
@@ -65,6 +65,7 @@ static const struct meson_gx_package_id {
{ "A113X", 0x25, 0x37, 0xff },
{ "A113D", 0x25, 0x22, 0xff },
{ "S905D2", 0x28, 0x10, 0xf0 },
+ { "S905Y2", 0x28, 0x30, 0xf0 },
{ "S905X2", 0x28, 0x40, 0xf0 },
{ "A311D", 0x29, 0x10, 0xf0 },
{ "S922X", 0x29, 0x40, 0xf0 },
diff --git a/drivers/soc/aspeed/Kconfig b/drivers/soc/aspeed/Kconfig
index 243ca196e6ad..f579ee0b5afa 100644
--- a/drivers/soc/aspeed/Kconfig
+++ b/drivers/soc/aspeed/Kconfig
@@ -24,6 +24,16 @@ config ASPEED_LPC_SNOOP
allows the BMC to listen on and save the data written by
the host to an arbitrary LPC I/O port.
+config ASPEED_UART_ROUTING
+ tristate "ASPEED uart routing control"
+ select REGMAP
+ select MFD_SYSCON
+ default ARCH_ASPEED
+ help
+ Provides a driver to control the UART routing paths, allowing
+ users to perform runtime configuration of the RX muxes among
+ the UART controllers and I/O pins.
+
config ASPEED_P2A_CTRL
tristate "ASPEED P2A (VGA MMIO to BMC) bridge control"
select REGMAP
diff --git a/drivers/soc/aspeed/Makefile b/drivers/soc/aspeed/Makefile
index fcab7192e1a4..b35d74592964 100644
--- a/drivers/soc/aspeed/Makefile
+++ b/drivers/soc/aspeed/Makefile
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
-obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o
-obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o
-obj-$(CONFIG_ASPEED_P2A_CTRL) += aspeed-p2a-ctrl.o
-obj-$(CONFIG_ASPEED_SOCINFO) += aspeed-socinfo.o
+obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o
+obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o
+obj-$(CONFIG_ASPEED_UART_ROUTING) += aspeed-uart-routing.o
+obj-$(CONFIG_ASPEED_P2A_CTRL) += aspeed-p2a-ctrl.o
+obj-$(CONFIG_ASPEED_SOCINFO) += aspeed-socinfo.o
diff --git a/drivers/soc/aspeed/aspeed-uart-routing.c b/drivers/soc/aspeed/aspeed-uart-routing.c
new file mode 100644
index 000000000000..ef8b24fd1851
--- /dev/null
+++ b/drivers/soc/aspeed/aspeed-uart-routing.c
@@ -0,0 +1,603 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2018 Google LLC
+ * Copyright (c) 2021 Aspeed Technology Inc.
+ */
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/platform_device.h>
+
+/* register offsets */
+#define HICR9 0x98
+#define HICRA 0x9c
+
+/* attributes options */
+#define UART_ROUTING_IO1 "io1"
+#define UART_ROUTING_IO2 "io2"
+#define UART_ROUTING_IO3 "io3"
+#define UART_ROUTING_IO4 "io4"
+#define UART_ROUTING_IO5 "io5"
+#define UART_ROUTING_IO6 "io6"
+#define UART_ROUTING_IO10 "io10"
+#define UART_ROUTING_UART1 "uart1"
+#define UART_ROUTING_UART2 "uart2"
+#define UART_ROUTING_UART3 "uart3"
+#define UART_ROUTING_UART4 "uart4"
+#define UART_ROUTING_UART5 "uart5"
+#define UART_ROUTING_UART6 "uart6"
+#define UART_ROUTING_UART10 "uart10"
+#define UART_ROUTING_RES "reserved"
+
+struct aspeed_uart_routing {
+ struct regmap *map;
+ struct attribute_group const *attr_grp;
+};
+
+struct aspeed_uart_routing_selector {
+ struct device_attribute dev_attr;
+ uint8_t reg;
+ uint8_t mask;
+ uint8_t shift;
+ const char *const options[];
+};
+
+#define to_routing_selector(_dev_attr) \
+ container_of(_dev_attr, struct aspeed_uart_routing_selector, dev_attr)
+
+static ssize_t aspeed_uart_routing_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t aspeed_uart_routing_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+#define ROUTING_ATTR(_name) { \
+ .attr = {.name = _name, \
+ .mode = VERIFY_OCTAL_PERMISSIONS(0644) }, \
+ .show = aspeed_uart_routing_show, \
+ .store = aspeed_uart_routing_store, \
+}
+
+/* routing selector for AST25xx */
+static struct aspeed_uart_routing_selector ast2500_io6_sel = {
+ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO6),
+ .reg = HICR9,
+ .shift = 8,
+ .mask = 0xf,
+ .options = {
+ UART_ROUTING_UART1,
+ UART_ROUTING_UART2,
+ UART_ROUTING_UART3,
+ UART_ROUTING_UART4,
+ UART_ROUTING_UART5,
+ UART_ROUTING_IO1,
+ UART_ROUTING_IO2,
+ UART_ROUTING_IO3,
+ UART_ROUTING_IO4,
+ UART_ROUTING_IO5,
+ NULL,
+ },
+};
+
+static struct aspeed_uart_routing_selector ast2500_uart5_sel = {
+ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART5),
+ .reg = HICRA,
+ .shift = 28,
+ .mask = 0xf,
+ .options = {
+ UART_ROUTING_IO5,
+ UART_ROUTING_IO1,
+ UART_ROUTING_IO2,
+ UART_ROUTING_IO3,
+ UART_ROUTING_IO4,
+ UART_ROUTING_UART1,
+ UART_ROUTING_UART2,
+ UART_ROUTING_UART3,
+ UART_ROUTING_UART4,
+ UART_ROUTING_IO6,
+ NULL,
+ },
+};
+
+static struct aspeed_uart_routing_selector ast2500_uart4_sel = {
+ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART4),
+ .reg = HICRA,
+ .shift = 25,
+ .mask = 0x7,
+ .options = {
+ UART_ROUTING_IO4,
+ UART_ROUTING_IO1,
+ UART_ROUTING_IO2,
+ UART_ROUTING_IO3,
+ UART_ROUTING_UART1,
+ UART_ROUTING_UART2,
+ UART_ROUTING_UART3,
+ UART_ROUTING_IO6,
+ NULL,
+ },
+};
+
+static struct aspeed_uart_routing_selector ast2500_uart3_sel = {
+ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART3),
+ .reg = HICRA,
+ .shift = 22,
+ .mask = 0x7,
+ .options = {
+ UART_ROUTING_IO3,
+ UART_ROUTING_IO4,
+ UART_ROUTING_IO1,
+ UART_ROUTING_IO2,
+ UART_ROUTING_UART4,
+ UART_ROUTING_UART1,
+ UART_ROUTING_UART2,
+ UART_ROUTING_IO6,
+ NULL,
+ },
+};
+
+static struct aspeed_uart_routing_selector ast2500_uart2_sel = {
+ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART2),
+ .reg = HICRA,
+ .shift = 19,
+ .mask = 0x7,
+ .options = {
+ UART_ROUTING_IO2,
+ UART_ROUTING_IO3,
+ UART_ROUTING_IO4,
+ UART_ROUTING_IO1,
+ UART_ROUTING_UART3,
+ UART_ROUTING_UART4,
+ UART_ROUTING_UART1,
+ UART_ROUTING_IO6,
+ NULL,
+ },
+};
+
+static struct aspeed_uart_routing_selector ast2500_uart1_sel = {
+ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART1),
+ .reg = HICRA,
+ .shift = 16,
+ .mask = 0x7,
+ .options = {
+ UART_ROUTING_IO1,
+ UART_ROUTING_IO2,
+ UART_ROUTING_IO3,
+ UART_ROUTING_IO4,
+ UART_ROUTING_UART2,
+ UART_ROUTING_UART3,
+ UART_ROUTING_UART4,
+ UART_ROUTING_IO6,
+ NULL,
+ },
+};
+
+static struct aspeed_uart_routing_selector ast2500_io5_sel = {
+ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO5),
+ .reg = HICRA,
+ .shift = 12,
+ .mask = 0x7,
+ .options = {
+ UART_ROUTING_UART5,
+ UART_ROUTING_UART1,
+ UART_ROUTING_UART2,
+ UART_ROUTING_UART3,
+ UART_ROUTING_UART4,
+ UART_ROUTING_IO1,
+ UART_ROUTING_IO3,
+ UART_ROUTING_IO6,
+ NULL,
+ },
+};
+
+static struct aspeed_uart_routing_selector ast2500_io4_sel = {
+ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO4),
+ .reg = HICRA,
+ .shift = 9,
+ .mask = 0x7,
+ .options = {
+ UART_ROUTING_UART4,
+ UART_ROUTING_UART5,
+ UART_ROUTING_UART1,
+ UART_ROUTING_UART2,
+ UART_ROUTING_UART3,
+ UART_ROUTING_IO1,
+ UART_ROUTING_IO2,
+ UART_ROUTING_IO6,
+ NULL,
+ },
+};
+
+static struct aspeed_uart_routing_selector ast2500_io3_sel = {
+ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO3),
+ .reg = HICRA,
+ .shift = 6,
+ .mask = 0x7,
+ .options = {
+ UART_ROUTING_UART3,
+ UART_ROUTING_UART4,
+ UART_ROUTING_UART5,
+ UART_ROUTING_UART1,
+ UART_ROUTING_UART2,
+ UART_ROUTING_IO1,
+ UART_ROUTING_IO2,
+ UART_ROUTING_IO6,
+ NULL,
+ },
+};
+
+static struct aspeed_uart_routing_selector ast2500_io2_sel = {
+ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO2),
+ .reg = HICRA,
+ .shift = 3,
+ .mask = 0x7,
+ .options = {
+ UART_ROUTING_UART2,
+ UART_ROUTING_UART3,
+ UART_ROUTING_UART4,
+ UART_ROUTING_UART5,
+ UART_ROUTING_UART1,
+ UART_ROUTING_IO3,
+ UART_ROUTING_IO4,
+ UART_ROUTING_IO6,
+ NULL,
+ },
+};
+
+static struct aspeed_uart_routing_selector ast2500_io1_sel = {
+ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO1),
+ .reg = HICRA,
+ .shift = 0,
+ .mask = 0x7,
+ .options = {
+ UART_ROUTING_UART1,
+ UART_ROUTING_UART2,
+ UART_ROUTING_UART3,
+ UART_ROUTING_UART4,
+ UART_ROUTING_UART5,
+ UART_ROUTING_IO3,
+ UART_ROUTING_IO4,
+ UART_ROUTING_IO6,
+ NULL,
+ },
+};
+
+static struct attribute *ast2500_uart_routing_attrs[] = {
+ &ast2500_io6_sel.dev_attr.attr,
+ &ast2500_uart5_sel.dev_attr.attr,
+ &ast2500_uart4_sel.dev_attr.attr,
+ &ast2500_uart3_sel.dev_attr.attr,
+ &ast2500_uart2_sel.dev_attr.attr,
+ &ast2500_uart1_sel.dev_attr.attr,
+ &ast2500_io5_sel.dev_attr.attr,
+ &ast2500_io4_sel.dev_attr.attr,
+ &ast2500_io3_sel.dev_attr.attr,
+ &ast2500_io2_sel.dev_attr.attr,
+ &ast2500_io1_sel.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ast2500_uart_routing_attr_group = {
+ .attrs = ast2500_uart_routing_attrs,
+};
+
+/* routing selector for AST26xx */
+static struct aspeed_uart_routing_selector ast2600_uart10_sel = {
+ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART10),
+ .reg = HICR9,
+ .shift = 12,
+ .mask = 0xf,
+ .options = {
+ UART_ROUTING_IO10,
+ UART_ROUTING_IO1,
+ UART_ROUTING_IO2,
+ UART_ROUTING_IO3,
+ UART_ROUTING_IO4,
+ UART_ROUTING_RES,
+ UART_ROUTING_UART1,
+ UART_ROUTING_UART2,
+ UART_ROUTING_UART3,
+ UART_ROUTING_UART4,
+ NULL,
+ },
+};
+
+static struct aspeed_uart_routing_selector ast2600_io10_sel = {
+ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO10),
+ .reg = HICR9,
+ .shift = 8,
+ .mask = 0xf,
+ .options = {
+ UART_ROUTING_UART1,
+ UART_ROUTING_UART2,
+ UART_ROUTING_UART3,
+ UART_ROUTING_UART4,
+ UART_ROUTING_RES,
+ UART_ROUTING_IO1,
+ UART_ROUTING_IO2,
+ UART_ROUTING_IO3,
+ UART_ROUTING_IO4,
+ UART_ROUTING_RES,
+ UART_ROUTING_UART10,
+ NULL,
+ },
+};
+
+static struct aspeed_uart_routing_selector ast2600_uart4_sel = {
+ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART4),
+ .reg = HICRA,
+ .shift = 25,
+ .mask = 0x7,
+ .options = {
+ UART_ROUTING_IO4,
+ UART_ROUTING_IO1,
+ UART_ROUTING_IO2,
+ UART_ROUTING_IO3,
+ UART_ROUTING_UART1,
+ UART_ROUTING_UART2,
+ UART_ROUTING_UART3,
+ UART_ROUTING_IO10,
+ NULL,
+ },
+};
+
+static struct aspeed_uart_routing_selector ast2600_uart3_sel = {
+ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART3),
+ .reg = HICRA,
+ .shift = 22,
+ .mask = 0x7,
+ .options = {
+ UART_ROUTING_IO3,
+ UART_ROUTING_IO4,
+ UART_ROUTING_IO1,
+ UART_ROUTING_IO2,
+ UART_ROUTING_UART4,
+ UART_ROUTING_UART1,
+ UART_ROUTING_UART2,
+ UART_ROUTING_IO10,
+ NULL,
+ },
+};
+
+static struct aspeed_uart_routing_selector ast2600_uart2_sel = {
+ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART2),
+ .reg = HICRA,
+ .shift = 19,
+ .mask = 0x7,
+ .options = {
+ UART_ROUTING_IO2,
+ UART_ROUTING_IO3,
+ UART_ROUTING_IO4,
+ UART_ROUTING_IO1,
+ UART_ROUTING_UART3,
+ UART_ROUTING_UART4,
+ UART_ROUTING_UART1,
+ UART_ROUTING_IO10,
+ NULL,
+ },
+};
+
+static struct aspeed_uart_routing_selector ast2600_uart1_sel = {
+ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART1),
+ .reg = HICRA,
+ .shift = 16,
+ .mask = 0x7,
+ .options = {
+ UART_ROUTING_IO1,
+ UART_ROUTING_IO2,
+ UART_ROUTING_IO3,
+ UART_ROUTING_IO4,
+ UART_ROUTING_UART2,
+ UART_ROUTING_UART3,
+ UART_ROUTING_UART4,
+ UART_ROUTING_IO10,
+ NULL,
+ },
+};
+
+static struct aspeed_uart_routing_selector ast2600_io4_sel = {
+ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO4),
+ .reg = HICRA,
+ .shift = 9,
+ .mask = 0x7,
+ .options = {
+ UART_ROUTING_UART4,
+ UART_ROUTING_UART10,
+ UART_ROUTING_UART1,
+ UART_ROUTING_UART2,
+ UART_ROUTING_UART3,
+ UART_ROUTING_IO1,
+ UART_ROUTING_IO2,
+ UART_ROUTING_IO10,
+ NULL,
+ },
+};
+
+static struct aspeed_uart_routing_selector ast2600_io3_sel = {
+ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO3),
+ .reg = HICRA,
+ .shift = 6,
+ .mask = 0x7,
+ .options = {
+ UART_ROUTING_UART3,
+ UART_ROUTING_UART4,
+ UART_ROUTING_UART10,
+ UART_ROUTING_UART1,
+ UART_ROUTING_UART2,
+ UART_ROUTING_IO1,
+ UART_ROUTING_IO2,
+ UART_ROUTING_IO10,
+ NULL,
+ },
+};
+
+static struct aspeed_uart_routing_selector ast2600_io2_sel = {
+ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO2),
+ .reg = HICRA,
+ .shift = 3,
+ .mask = 0x7,
+ .options = {
+ UART_ROUTING_UART2,
+ UART_ROUTING_UART3,
+ UART_ROUTING_UART4,
+ UART_ROUTING_UART10,
+ UART_ROUTING_UART1,
+ UART_ROUTING_IO3,
+ UART_ROUTING_IO4,
+ UART_ROUTING_IO10,
+ NULL,
+ },
+};
+
+static struct aspeed_uart_routing_selector ast2600_io1_sel = {
+ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO1),
+ .reg = HICRA,
+ .shift = 0,
+ .mask = 0x7,
+ .options = {
+ UART_ROUTING_UART1,
+ UART_ROUTING_UART2,
+ UART_ROUTING_UART3,
+ UART_ROUTING_UART4,
+ UART_ROUTING_UART10,
+ UART_ROUTING_IO3,
+ UART_ROUTING_IO4,
+ UART_ROUTING_IO10,
+ NULL,
+ },
+};
+
+static struct attribute *ast2600_uart_routing_attrs[] = {
+ &ast2600_uart10_sel.dev_attr.attr,
+ &ast2600_io10_sel.dev_attr.attr,
+ &ast2600_uart4_sel.dev_attr.attr,
+ &ast2600_uart3_sel.dev_attr.attr,
+ &ast2600_uart2_sel.dev_attr.attr,
+ &ast2600_uart1_sel.dev_attr.attr,
+ &ast2600_io4_sel.dev_attr.attr,
+ &ast2600_io3_sel.dev_attr.attr,
+ &ast2600_io2_sel.dev_attr.attr,
+ &ast2600_io1_sel.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ast2600_uart_routing_attr_group = {
+ .attrs = ast2600_uart_routing_attrs,
+};
+
+static ssize_t aspeed_uart_routing_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct aspeed_uart_routing *uart_routing = dev_get_drvdata(dev);
+ struct aspeed_uart_routing_selector *sel = to_routing_selector(attr);
+ int val, pos, len;
+
+ regmap_read(uart_routing->map, sel->reg, &val);
+ val = (val >> sel->shift) & sel->mask;
+
+ len = 0;
+ for (pos = 0; sel->options[pos] != NULL; ++pos) {
+ if (pos == val)
+ len += sysfs_emit_at(buf, len, "[%s] ", sel->options[pos]);
+ else
+ len += sysfs_emit_at(buf, len, "%s ", sel->options[pos]);
+ }
+
+ if (val >= pos)
+ len += sysfs_emit_at(buf, len, "[unknown(%d)]", val);
+
+ len += sysfs_emit_at(buf, len, "\n");
+
+ return len;
+}
+
+static ssize_t aspeed_uart_routing_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct aspeed_uart_routing *uart_routing = dev_get_drvdata(dev);
+ struct aspeed_uart_routing_selector *sel = to_routing_selector(attr);
+ int val;
+
+ val = match_string(sel->options, -1, buf);
+ if (val < 0) {
+ dev_err(dev, "invalid value \"%s\"\n", buf);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(uart_routing->map, sel->reg,
+ (sel->mask << sel->shift),
+ (val & sel->mask) << sel->shift);
+
+ return count;
+}
+
+static int aspeed_uart_routing_probe(struct platform_device *pdev)
+{
+ int rc;
+ struct device *dev = &pdev->dev;
+ struct aspeed_uart_routing *uart_routing;
+
+ uart_routing = devm_kzalloc(&pdev->dev, sizeof(*uart_routing), GFP_KERNEL);
+ if (!uart_routing)
+ return -ENOMEM;
+
+ uart_routing->map = syscon_node_to_regmap(dev->parent->of_node);
+ if (IS_ERR(uart_routing->map)) {
+ dev_err(dev, "cannot get regmap\n");
+ return PTR_ERR(uart_routing->map);
+ }
+
+ uart_routing->attr_grp = of_device_get_match_data(dev);
+
+ rc = sysfs_create_group(&dev->kobj, uart_routing->attr_grp);
+ if (rc < 0)
+ return rc;
+
+ dev_set_drvdata(dev, uart_routing);
+
+ dev_info(dev, "module loaded\n");
+
+ return 0;
+}
+
+static int aspeed_uart_routing_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct aspeed_uart_routing *uart_routing = platform_get_drvdata(pdev);
+
+ sysfs_remove_group(&dev->kobj, uart_routing->attr_grp);
+
+ return 0;
+}
+
+static const struct of_device_id aspeed_uart_routing_table[] = {
+ { .compatible = "aspeed,ast2400-uart-routing",
+ .data = &ast2500_uart_routing_attr_group },
+ { .compatible = "aspeed,ast2500-uart-routing",
+ .data = &ast2500_uart_routing_attr_group },
+ { .compatible = "aspeed,ast2600-uart-routing",
+ .data = &ast2600_uart_routing_attr_group },
+ { },
+};
+
+static struct platform_driver aspeed_uart_routing_driver = {
+ .driver = {
+ .name = "aspeed-uart-routing",
+ .of_match_table = aspeed_uart_routing_table,
+ },
+ .probe = aspeed_uart_routing_probe,
+ .remove = aspeed_uart_routing_remove,
+};
+
+module_platform_driver(aspeed_uart_routing_driver);
+
+MODULE_AUTHOR("Oskar Senft <osk@google.com>");
+MODULE_AUTHOR("Chia-Wei Wang <chiawei_wang@aspeedtech.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Driver to configure Aspeed UART routing");
diff --git a/drivers/soc/bcm/bcm63xx/bcm-pmb.c b/drivers/soc/bcm/bcm63xx/bcm-pmb.c
index 774465c119be..7bbe46ea5f94 100644
--- a/drivers/soc/bcm/bcm63xx/bcm-pmb.c
+++ b/drivers/soc/bcm/bcm63xx/bcm-pmb.c
@@ -276,7 +276,6 @@ static int bcm_pmb_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
const struct bcm_pmb_pd_data *table;
const struct bcm_pmb_pd_data *e;
- struct resource *res;
struct bcm_pmb *pmb;
int max_id;
int err;
@@ -287,8 +286,7 @@ static int bcm_pmb_probe(struct platform_device *pdev)
pmb->dev = dev;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- pmb->base = devm_ioremap_resource(&pdev->dev, res);
+ pmb->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(pmb->base))
return PTR_ERR(pmb->base);
diff --git a/drivers/soc/bcm/bcm63xx/bcm63xx-power.c b/drivers/soc/bcm/bcm63xx/bcm63xx-power.c
index 515fe182dc34..aa72e13d5d0e 100644
--- a/drivers/soc/bcm/bcm63xx/bcm63xx-power.c
+++ b/drivers/soc/bcm/bcm63xx/bcm63xx-power.c
@@ -91,7 +91,6 @@ static int bcm63xx_power_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
- struct resource *res;
const struct bcm63xx_power_data *entry, *table;
struct bcm63xx_power *power;
unsigned int ndom;
@@ -102,8 +101,7 @@ static int bcm63xx_power_probe(struct platform_device *pdev)
if (!power)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- power->base = devm_ioremap_resource(&pdev->dev, res);
+ power->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(power->base))
return PTR_ERR(power->base);
diff --git a/drivers/soc/bcm/brcmstb/biuctrl.c b/drivers/soc/bcm/brcmstb/biuctrl.c
index 7f8dc302ae6e..2c975d79fe8e 100644
--- a/drivers/soc/bcm/brcmstb/biuctrl.c
+++ b/drivers/soc/bcm/brcmstb/biuctrl.c
@@ -136,6 +136,8 @@ static int __init mcp_write_pairing_set(void)
static const u32 a72_b53_mach_compat[] = {
0x7211,
+ 0x72113,
+ 0x72116,
0x7216,
0x72164,
0x72165,
diff --git a/drivers/soc/fsl/Kconfig b/drivers/soc/fsl/Kconfig
index 4df32bc4c7a6..07d52cafbb31 100644
--- a/drivers/soc/fsl/Kconfig
+++ b/drivers/soc/fsl/Kconfig
@@ -24,6 +24,7 @@ config FSL_MC_DPIO
tristate "QorIQ DPAA2 DPIO driver"
depends on FSL_MC_BUS
select SOC_BUS
+ select DIMLIB
help
Driver for the DPAA2 DPIO object. A DPIO provides queue and
buffer management facilities for software to interact with
diff --git a/drivers/soc/fsl/dpio/dpio-cmd.h b/drivers/soc/fsl/dpio/dpio-cmd.h
index e13fd3ac1939..2fbcb78cdaaf 100644
--- a/drivers/soc/fsl/dpio/dpio-cmd.h
+++ b/drivers/soc/fsl/dpio/dpio-cmd.h
@@ -46,6 +46,9 @@ struct dpio_rsp_get_attr {
__le64 qbman_portal_ci_addr;
/* cmd word 3 */
__le32 qbman_version;
+ __le32 pad1;
+ /* cmd word 4 */
+ __le32 clk;
};
struct dpio_stashing_dest {
diff --git a/drivers/soc/fsl/dpio/dpio-driver.c b/drivers/soc/fsl/dpio/dpio-driver.c
index 7f397b4ad878..dd948889eeab 100644
--- a/drivers/soc/fsl/dpio/dpio-driver.c
+++ b/drivers/soc/fsl/dpio/dpio-driver.c
@@ -162,6 +162,7 @@ static int dpaa2_dpio_probe(struct fsl_mc_device *dpio_dev)
goto err_get_attr;
}
desc.qman_version = dpio_attrs.qbman_version;
+ desc.qman_clk = dpio_attrs.clk;
err = dpio_enable(dpio_dev->mc_io, 0, dpio_dev->mc_handle);
if (err) {
diff --git a/drivers/soc/fsl/dpio/dpio-service.c b/drivers/soc/fsl/dpio/dpio-service.c
index 779c319a4b82..1d2b27e3ea63 100644
--- a/drivers/soc/fsl/dpio/dpio-service.c
+++ b/drivers/soc/fsl/dpio/dpio-service.c
@@ -12,6 +12,7 @@
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
+#include <linux/dim.h>
#include <linux/slab.h>
#include "dpio.h"
@@ -28,6 +29,14 @@ struct dpaa2_io {
spinlock_t lock_notifications;
struct list_head notifications;
struct device *dev;
+
+ /* Net DIM */
+ struct dim rx_dim;
+ /* protect against concurrent Net DIM updates */
+ spinlock_t dim_lock;
+ u16 event_ctr;
+ u64 bytes;
+ u64 frames;
};
struct dpaa2_io_store {
@@ -100,6 +109,17 @@ struct dpaa2_io *dpaa2_io_service_select(int cpu)
}
EXPORT_SYMBOL_GPL(dpaa2_io_service_select);
+static void dpaa2_io_dim_work(struct work_struct *w)
+{
+ struct dim *dim = container_of(w, struct dim, work);
+ struct dim_cq_moder moder =
+ net_dim_get_rx_moderation(dim->mode, dim->profile_ix);
+ struct dpaa2_io *d = container_of(dim, struct dpaa2_io, rx_dim);
+
+ dpaa2_io_set_irq_coalescing(d, moder.usec);
+ dim->state = DIM_START_MEASURE;
+}
+
/**
* dpaa2_io_create() - create a dpaa2_io object.
* @desc: the dpaa2_io descriptor
@@ -114,6 +134,7 @@ struct dpaa2_io *dpaa2_io_create(const struct dpaa2_io_desc *desc,
struct device *dev)
{
struct dpaa2_io *obj = kmalloc(sizeof(*obj), GFP_KERNEL);
+ u32 qman_256_cycles_per_ns;
if (!obj)
return NULL;
@@ -127,7 +148,15 @@ struct dpaa2_io *dpaa2_io_create(const struct dpaa2_io_desc *desc,
obj->dpio_desc = *desc;
obj->swp_desc.cena_bar = obj->dpio_desc.regs_cena;
obj->swp_desc.cinh_bar = obj->dpio_desc.regs_cinh;
+ obj->swp_desc.qman_clk = obj->dpio_desc.qman_clk;
obj->swp_desc.qman_version = obj->dpio_desc.qman_version;
+
+ /* Compute how many 256 QBMAN cycles fit into one ns. This is because
+ * the interrupt timeout period register needs to be specified in QBMAN
+ * clock cycles in increments of 256.
+ */
+ qman_256_cycles_per_ns = 256000 / (obj->swp_desc.qman_clk / 1000000);
+ obj->swp_desc.qman_256_cycles_per_ns = qman_256_cycles_per_ns;
obj->swp = qbman_swp_init(&obj->swp_desc);
if (!obj->swp) {
@@ -138,6 +167,7 @@ struct dpaa2_io *dpaa2_io_create(const struct dpaa2_io_desc *desc,
INIT_LIST_HEAD(&obj->node);
spin_lock_init(&obj->lock_mgmt_cmd);
spin_lock_init(&obj->lock_notifications);
+ spin_lock_init(&obj->dim_lock);
INIT_LIST_HEAD(&obj->notifications);
/* For now only enable DQRR interrupts */
@@ -155,6 +185,12 @@ struct dpaa2_io *dpaa2_io_create(const struct dpaa2_io_desc *desc,
obj->dev = dev;
+ memset(&obj->rx_dim, 0, sizeof(obj->rx_dim));
+ INIT_WORK(&obj->rx_dim.work, dpaa2_io_dim_work);
+ obj->event_ctr = 0;
+ obj->bytes = 0;
+ obj->frames = 0;
+
return obj;
}
@@ -194,6 +230,8 @@ irqreturn_t dpaa2_io_irq(struct dpaa2_io *obj)
struct qbman_swp *swp;
u32 status;
+ obj->event_ctr++;
+
swp = obj->swp;
status = qbman_swp_interrupt_read_status(swp);
if (!status)
@@ -462,7 +500,7 @@ int dpaa2_io_service_enqueue_multiple_fq(struct dpaa2_io *d,
qbman_eq_desc_set_no_orp(&ed, 0);
qbman_eq_desc_set_fq(&ed, fqid);
- return qbman_swp_enqueue_multiple(d->swp, &ed, fd, 0, nb);
+ return qbman_swp_enqueue_multiple(d->swp, &ed, fd, NULL, nb);
}
EXPORT_SYMBOL(dpaa2_io_service_enqueue_multiple_fq);
@@ -779,3 +817,82 @@ int dpaa2_io_query_bp_count(struct dpaa2_io *d, u16 bpid, u32 *num)
return 0;
}
EXPORT_SYMBOL_GPL(dpaa2_io_query_bp_count);
+
+/**
+ * dpaa2_io_set_irq_coalescing() - Set new IRQ coalescing values
+ * @d: the given DPIO object
+ * @irq_holdoff: interrupt holdoff (timeout) period in us
+ *
+ * Return 0 for success, or negative error code on error.
+ */
+int dpaa2_io_set_irq_coalescing(struct dpaa2_io *d, u32 irq_holdoff)
+{
+ struct qbman_swp *swp = d->swp;
+
+ return qbman_swp_set_irq_coalescing(swp, swp->dqrr.dqrr_size - 1,
+ irq_holdoff);
+}
+EXPORT_SYMBOL(dpaa2_io_set_irq_coalescing);
+
+/**
+ * dpaa2_io_get_irq_coalescing() - Get the current IRQ coalescing parameters
+ * @d: the given DPIO object
+ * @irq_holdoff: interrupt holdoff (timeout) period in us
+ */
+void dpaa2_io_get_irq_coalescing(struct dpaa2_io *d, u32 *irq_holdoff)
+{
+ struct qbman_swp *swp = d->swp;
+
+ qbman_swp_get_irq_coalescing(swp, NULL, irq_holdoff);
+}
+EXPORT_SYMBOL(dpaa2_io_get_irq_coalescing);
+
+/**
+ * dpaa2_io_set_adaptive_coalescing() - Enable/disable adaptive coalescing
+ * @d: the given DPIO object
+ * @use_adaptive_rx_coalesce: adaptive coalescing state
+ */
+void dpaa2_io_set_adaptive_coalescing(struct dpaa2_io *d,
+ int use_adaptive_rx_coalesce)
+{
+ d->swp->use_adaptive_rx_coalesce = use_adaptive_rx_coalesce;
+}
+EXPORT_SYMBOL(dpaa2_io_set_adaptive_coalescing);
+
+/**
+ * dpaa2_io_get_adaptive_coalescing() - Query adaptive coalescing state
+ * @d: the given DPIO object
+ *
+ * Return 1 when adaptive coalescing is enabled on the DPIO object and 0
+ * otherwise.
+ */
+int dpaa2_io_get_adaptive_coalescing(struct dpaa2_io *d)
+{
+ return d->swp->use_adaptive_rx_coalesce;
+}
+EXPORT_SYMBOL(dpaa2_io_get_adaptive_coalescing);
+
+/**
+ * dpaa2_io_update_net_dim() - Update Net DIM
+ * @d: the given DPIO object
+ * @frames: how many frames have been dequeued by the user since the last call
+ * @bytes: how many bytes have been dequeued by the user since the last call
+ */
+void dpaa2_io_update_net_dim(struct dpaa2_io *d, __u64 frames, __u64 bytes)
+{
+ struct dim_sample dim_sample = {};
+
+ if (!d->swp->use_adaptive_rx_coalesce)
+ return;
+
+ spin_lock(&d->dim_lock);
+
+ d->bytes += bytes;
+ d->frames += frames;
+
+ dim_update_sample(d->event_ctr, d->frames, d->bytes, &dim_sample);
+ net_dim(&d->rx_dim, dim_sample);
+
+ spin_unlock(&d->dim_lock);
+}
+EXPORT_SYMBOL(dpaa2_io_update_net_dim);
diff --git a/drivers/soc/fsl/dpio/dpio.c b/drivers/soc/fsl/dpio/dpio.c
index af74c597a675..8ed606ffaac5 100644
--- a/drivers/soc/fsl/dpio/dpio.c
+++ b/drivers/soc/fsl/dpio/dpio.c
@@ -162,6 +162,7 @@ int dpio_get_attributes(struct fsl_mc_io *mc_io,
attr->qbman_portal_ci_offset =
le64_to_cpu(dpio_rsp->qbman_portal_ci_addr);
attr->qbman_version = le32_to_cpu(dpio_rsp->qbman_version);
+ attr->clk = le32_to_cpu(dpio_rsp->clk);
return 0;
}
diff --git a/drivers/soc/fsl/dpio/dpio.h b/drivers/soc/fsl/dpio/dpio.h
index da06f7258098..7fda44f0d7f4 100644
--- a/drivers/soc/fsl/dpio/dpio.h
+++ b/drivers/soc/fsl/dpio/dpio.h
@@ -59,6 +59,7 @@ int dpio_disable(struct fsl_mc_io *mc_io,
* @num_priorities: Number of priorities for the notification channel (1-8);
* relevant only if 'channel_mode = DPIO_LOCAL_CHANNEL'
* @qbman_version: QBMAN version
+ * @clk: QBMAN clock frequency value in Hz
*/
struct dpio_attr {
int id;
@@ -68,6 +69,7 @@ struct dpio_attr {
enum dpio_channel_mode channel_mode;
u8 num_priorities;
u32 qbman_version;
+ u32 clk;
};
int dpio_get_attributes(struct fsl_mc_io *mc_io,
diff --git a/drivers/soc/fsl/dpio/qbman-portal.c b/drivers/soc/fsl/dpio/qbman-portal.c
index 3ec8ab08b988..058b78fac5e3 100644
--- a/drivers/soc/fsl/dpio/qbman-portal.c
+++ b/drivers/soc/fsl/dpio/qbman-portal.c
@@ -29,6 +29,7 @@
#define QBMAN_CINH_SWP_EQCR_AM_RT 0x980
#define QBMAN_CINH_SWP_RCR_AM_RT 0x9c0
#define QBMAN_CINH_SWP_DQPI 0xa00
+#define QBMAN_CINH_SWP_DQRR_ITR 0xa80
#define QBMAN_CINH_SWP_DCAP 0xac0
#define QBMAN_CINH_SWP_SDQCR 0xb00
#define QBMAN_CINH_SWP_EQCR_AM_RT2 0xb40
@@ -38,6 +39,7 @@
#define QBMAN_CINH_SWP_IER 0xe40
#define QBMAN_CINH_SWP_ISDR 0xe80
#define QBMAN_CINH_SWP_IIR 0xec0
+#define QBMAN_CINH_SWP_ITPR 0xf40
/* CENA register offsets */
#define QBMAN_CENA_SWP_EQCR(n) (0x000 + ((u32)(n) << 6))
@@ -355,6 +357,9 @@ struct qbman_swp *qbman_swp_init(const struct qbman_swp_desc *d)
& p->eqcr.pi_ci_mask;
p->eqcr.available = p->eqcr.pi_ring_size;
+ /* Initialize the software portal with a irq timeout period of 0us */
+ qbman_swp_set_irq_coalescing(p, p->dqrr.dqrr_size - 1, 0);
+
return p;
}
@@ -688,9 +693,9 @@ int qbman_swp_enqueue_multiple_direct(struct qbman_swp *s,
p = (s->addr_cena + QBMAN_CENA_SWP_EQCR(eqcr_pi & half_mask));
p[0] = cl[0] | s->eqcr.pi_vb;
if (flags && (flags[i] & QBMAN_ENQUEUE_FLAG_DCA)) {
- struct qbman_eq_desc *d = (struct qbman_eq_desc *)p;
+ struct qbman_eq_desc *eq_desc = (struct qbman_eq_desc *)p;
- d->dca = (1 << QB_ENQUEUE_CMD_DCA_EN_SHIFT) |
+ eq_desc->dca = (1 << QB_ENQUEUE_CMD_DCA_EN_SHIFT) |
((flags[i]) & QBMAN_EQCR_DCA_IDXMASK);
}
eqcr_pi++;
@@ -768,9 +773,9 @@ int qbman_swp_enqueue_multiple_mem_back(struct qbman_swp *s,
p = (s->addr_cena + QBMAN_CENA_SWP_EQCR(eqcr_pi & half_mask));
p[0] = cl[0] | s->eqcr.pi_vb;
if (flags && (flags[i] & QBMAN_ENQUEUE_FLAG_DCA)) {
- struct qbman_eq_desc *d = (struct qbman_eq_desc *)p;
+ struct qbman_eq_desc *eq_desc = (struct qbman_eq_desc *)p;
- d->dca = (1 << QB_ENQUEUE_CMD_DCA_EN_SHIFT) |
+ eq_desc->dca = (1 << QB_ENQUEUE_CMD_DCA_EN_SHIFT) |
((flags[i]) & QBMAN_EQCR_DCA_IDXMASK);
}
eqcr_pi++;
@@ -1793,3 +1798,56 @@ u32 qbman_bp_info_num_free_bufs(struct qbman_bp_query_rslt *a)
{
return le32_to_cpu(a->fill);
}
+
+/**
+ * qbman_swp_set_irq_coalescing() - Set new IRQ coalescing values
+ * @p: the software portal object
+ * @irq_threshold: interrupt threshold
+ * @irq_holdoff: interrupt holdoff (timeout) period in us
+ *
+ * Return 0 for success, or negative error code on error.
+ */
+int qbman_swp_set_irq_coalescing(struct qbman_swp *p, u32 irq_threshold,
+ u32 irq_holdoff)
+{
+ u32 itp, max_holdoff;
+
+ /* Convert irq_holdoff value from usecs to 256 QBMAN clock cycles
+ * increments. This depends on the QBMAN internal frequency.
+ */
+ itp = (irq_holdoff * 1000) / p->desc->qman_256_cycles_per_ns;
+ if (itp > 4096) {
+ max_holdoff = (p->desc->qman_256_cycles_per_ns * 4096) / 1000;
+ pr_err("irq_holdoff must be <= %uus\n", max_holdoff);
+ return -EINVAL;
+ }
+
+ if (irq_threshold >= p->dqrr.dqrr_size) {
+ pr_err("irq_threshold must be < %u\n", p->dqrr.dqrr_size - 1);
+ return -EINVAL;
+ }
+
+ p->irq_threshold = irq_threshold;
+ p->irq_holdoff = irq_holdoff;
+
+ qbman_write_register(p, QBMAN_CINH_SWP_DQRR_ITR, irq_threshold);
+ qbman_write_register(p, QBMAN_CINH_SWP_ITPR, itp);
+
+ return 0;
+}
+
+/**
+ * qbman_swp_get_irq_coalescing() - Get the current IRQ coalescing parameters
+ * @p: the software portal object
+ * @irq_threshold: interrupt threshold (an IRQ is generated when there are more
+ * DQRR entries in the portal than the threshold)
+ * @irq_holdoff: interrupt holdoff (timeout) period in us
+ */
+void qbman_swp_get_irq_coalescing(struct qbman_swp *p, u32 *irq_threshold,
+ u32 *irq_holdoff)
+{
+ if (irq_threshold)
+ *irq_threshold = p->irq_threshold;
+ if (irq_holdoff)
+ *irq_holdoff = p->irq_holdoff;
+}
diff --git a/drivers/soc/fsl/dpio/qbman-portal.h b/drivers/soc/fsl/dpio/qbman-portal.h
index c7c2225b7d91..b23883dd2725 100644
--- a/drivers/soc/fsl/dpio/qbman-portal.h
+++ b/drivers/soc/fsl/dpio/qbman-portal.h
@@ -24,6 +24,8 @@ struct qbman_swp_desc {
void *cena_bar; /* Cache-enabled portal base address */
void __iomem *cinh_bar; /* Cache-inhibited portal base address */
u32 qman_version;
+ u32 qman_clk;
+ u32 qman_256_cycles_per_ns;
};
#define QBMAN_SWP_INTERRUPT_EQRI 0x01
@@ -156,6 +158,11 @@ struct qbman_swp {
} eqcr;
spinlock_t access_spinlock;
+
+ /* Interrupt coalescing */
+ u32 irq_threshold;
+ u32 irq_holdoff;
+ int use_adaptive_rx_coalesce;
};
/* Function pointers */
@@ -648,4 +655,10 @@ static inline const struct dpaa2_dq *qbman_swp_dqrr_next(struct qbman_swp *s)
return qbman_swp_dqrr_next_ptr(s);
}
+int qbman_swp_set_irq_coalescing(struct qbman_swp *p, u32 irq_threshold,
+ u32 irq_holdoff);
+
+void qbman_swp_get_irq_coalescing(struct qbman_swp *p, u32 *irq_threshold,
+ u32 *irq_holdoff);
+
#endif /* __FSL_QBMAN_PORTAL_H */
diff --git a/drivers/soc/fsl/guts.c b/drivers/soc/fsl/guts.c
index d5e9a5f2c087..072473a16f4d 100644
--- a/drivers/soc/fsl/guts.c
+++ b/drivers/soc/fsl/guts.c
@@ -140,7 +140,6 @@ static int fsl_guts_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev;
- struct resource *res;
const struct fsl_soc_die_attr *soc_die;
const char *machine;
u32 svr;
@@ -152,8 +151,7 @@ static int fsl_guts_probe(struct platform_device *pdev)
guts->little_endian = of_property_read_bool(np, "little-endian");
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- guts->regs = devm_ioremap_resource(dev, res);
+ guts->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(guts->regs))
return PTR_ERR(guts->regs);
diff --git a/drivers/soc/fsl/rcpm.c b/drivers/soc/fsl/rcpm.c
index 90d3f4060b0c..3d0cae30c769 100644
--- a/drivers/soc/fsl/rcpm.c
+++ b/drivers/soc/fsl/rcpm.c
@@ -146,7 +146,6 @@ static const struct dev_pm_ops rcpm_pm_ops = {
static int rcpm_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct resource *r;
struct rcpm *rcpm;
int ret;
@@ -154,11 +153,7 @@ static int rcpm_probe(struct platform_device *pdev)
if (!rcpm)
return -ENOMEM;
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!r)
- return -ENODEV;
-
- rcpm->ippdexpcr_base = devm_ioremap_resource(&pdev->dev, r);
+ rcpm->ippdexpcr_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(rcpm->ippdexpcr_base)) {
ret = PTR_ERR(rcpm->ippdexpcr_base);
return ret;
diff --git a/drivers/soc/imx/Kconfig b/drivers/soc/imx/Kconfig
index 05812f8ae734..a840494e849a 100644
--- a/drivers/soc/imx/Kconfig
+++ b/drivers/soc/imx/Kconfig
@@ -6,6 +6,7 @@ config IMX_GPCV2_PM_DOMAINS
depends on ARCH_MXC || (COMPILE_TEST && OF)
depends on PM
select PM_GENERIC_DOMAINS
+ select REGMAP_MMIO
default y if SOC_IMX7D
config SOC_IMX8M
diff --git a/drivers/soc/imx/Makefile b/drivers/soc/imx/Makefile
index 078dc918f4f3..8a707077914c 100644
--- a/drivers/soc/imx/Makefile
+++ b/drivers/soc/imx/Makefile
@@ -5,3 +5,4 @@ endif
obj-$(CONFIG_HAVE_IMX_GPC) += gpc.o
obj-$(CONFIG_IMX_GPCV2_PM_DOMAINS) += gpcv2.o
obj-$(CONFIG_SOC_IMX8M) += soc-imx8m.o
+obj-$(CONFIG_SOC_IMX8M) += imx8m-blk-ctrl.o
diff --git a/drivers/soc/imx/gpcv2.c b/drivers/soc/imx/gpcv2.c
index 34a9ac1f2b9b..b8d52d8d29db 100644
--- a/drivers/soc/imx/gpcv2.c
+++ b/drivers/soc/imx/gpcv2.c
@@ -192,7 +192,7 @@ struct imx_pgc_domain {
struct clk_bulk_data *clks;
int num_clks;
- unsigned int pgc;
+ unsigned long pgc;
const struct {
u32 pxx;
@@ -202,6 +202,7 @@ struct imx_pgc_domain {
} bits;
const int voltage;
+ const bool keep_clocks;
struct device *dev;
};
@@ -220,7 +221,7 @@ to_imx_pgc_domain(struct generic_pm_domain *genpd)
static int imx_pgc_power_up(struct generic_pm_domain *genpd)
{
struct imx_pgc_domain *domain = to_imx_pgc_domain(genpd);
- u32 reg_val;
+ u32 reg_val, pgc;
int ret;
ret = pm_runtime_get_sync(domain->dev);
@@ -244,6 +245,8 @@ static int imx_pgc_power_up(struct generic_pm_domain *genpd)
goto out_regulator_disable;
}
+ reset_control_assert(domain->reset);
+
if (domain->bits.pxx) {
/* request the domain to power up */
regmap_update_bits(domain->regmap, GPC_PU_PGC_SW_PUP_REQ,
@@ -262,12 +265,12 @@ static int imx_pgc_power_up(struct generic_pm_domain *genpd)
}
/* disable power control */
- regmap_clear_bits(domain->regmap, GPC_PGC_CTRL(domain->pgc),
- GPC_PGC_CTRL_PCR);
+ for_each_set_bit(pgc, &domain->pgc, 32) {
+ regmap_clear_bits(domain->regmap, GPC_PGC_CTRL(pgc),
+ GPC_PGC_CTRL_PCR);
+ }
}
- reset_control_assert(domain->reset);
-
/* delay for reset to propagate */
udelay(5);
@@ -293,7 +296,8 @@ static int imx_pgc_power_up(struct generic_pm_domain *genpd)
}
/* Disable reset clocks for all devices in the domain */
- clk_bulk_disable_unprepare(domain->num_clks, domain->clks);
+ if (!domain->keep_clocks)
+ clk_bulk_disable_unprepare(domain->num_clks, domain->clks);
return 0;
@@ -311,14 +315,16 @@ out_put_pm:
static int imx_pgc_power_down(struct generic_pm_domain *genpd)
{
struct imx_pgc_domain *domain = to_imx_pgc_domain(genpd);
- u32 reg_val;
+ u32 reg_val, pgc;
int ret;
/* Enable reset clocks for all devices in the domain */
- ret = clk_bulk_prepare_enable(domain->num_clks, domain->clks);
- if (ret) {
- dev_err(domain->dev, "failed to enable reset clocks\n");
- return ret;
+ if (!domain->keep_clocks) {
+ ret = clk_bulk_prepare_enable(domain->num_clks, domain->clks);
+ if (ret) {
+ dev_err(domain->dev, "failed to enable reset clocks\n");
+ return ret;
+ }
}
/* request the ADB400 to power down */
@@ -338,8 +344,10 @@ static int imx_pgc_power_down(struct generic_pm_domain *genpd)
if (domain->bits.pxx) {
/* enable power control */
- regmap_update_bits(domain->regmap, GPC_PGC_CTRL(domain->pgc),
- GPC_PGC_CTRL_PCR, GPC_PGC_CTRL_PCR);
+ for_each_set_bit(pgc, &domain->pgc, 32) {
+ regmap_update_bits(domain->regmap, GPC_PGC_CTRL(pgc),
+ GPC_PGC_CTRL_PCR, GPC_PGC_CTRL_PCR);
+ }
/* request the domain to power down */
regmap_update_bits(domain->regmap, GPC_PU_PGC_SW_PDN_REQ,
@@ -389,7 +397,7 @@ static const struct imx_pgc_domain imx7_pgc_domains[] = {
.map = IMX7_MIPI_PHY_A_CORE_DOMAIN,
},
.voltage = 1000000,
- .pgc = IMX7_PGC_MIPI,
+ .pgc = BIT(IMX7_PGC_MIPI),
},
[IMX7_POWER_DOMAIN_PCIE_PHY] = {
@@ -401,7 +409,7 @@ static const struct imx_pgc_domain imx7_pgc_domains[] = {
.map = IMX7_PCIE_PHY_A_CORE_DOMAIN,
},
.voltage = 1000000,
- .pgc = IMX7_PGC_PCIE,
+ .pgc = BIT(IMX7_PGC_PCIE),
},
[IMX7_POWER_DOMAIN_USB_HSIC_PHY] = {
@@ -413,7 +421,7 @@ static const struct imx_pgc_domain imx7_pgc_domains[] = {
.map = IMX7_USB_HSIC_PHY_A_CORE_DOMAIN,
},
.voltage = 1200000,
- .pgc = IMX7_PGC_USB_HSIC,
+ .pgc = BIT(IMX7_PGC_USB_HSIC),
},
};
@@ -448,7 +456,7 @@ static const struct imx_pgc_domain imx8m_pgc_domains[] = {
.pxx = IMX8M_MIPI_SW_Pxx_REQ,
.map = IMX8M_MIPI_A53_DOMAIN,
},
- .pgc = IMX8M_PGC_MIPI,
+ .pgc = BIT(IMX8M_PGC_MIPI),
},
[IMX8M_POWER_DOMAIN_PCIE1] = {
@@ -459,7 +467,7 @@ static const struct imx_pgc_domain imx8m_pgc_domains[] = {
.pxx = IMX8M_PCIE1_SW_Pxx_REQ,
.map = IMX8M_PCIE1_A53_DOMAIN,
},
- .pgc = IMX8M_PGC_PCIE1,
+ .pgc = BIT(IMX8M_PGC_PCIE1),
},
[IMX8M_POWER_DOMAIN_USB_OTG1] = {
@@ -470,7 +478,7 @@ static const struct imx_pgc_domain imx8m_pgc_domains[] = {
.pxx = IMX8M_OTG1_SW_Pxx_REQ,
.map = IMX8M_OTG1_A53_DOMAIN,
},
- .pgc = IMX8M_PGC_OTG1,
+ .pgc = BIT(IMX8M_PGC_OTG1),
},
[IMX8M_POWER_DOMAIN_USB_OTG2] = {
@@ -481,7 +489,7 @@ static const struct imx_pgc_domain imx8m_pgc_domains[] = {
.pxx = IMX8M_OTG2_SW_Pxx_REQ,
.map = IMX8M_OTG2_A53_DOMAIN,
},
- .pgc = IMX8M_PGC_OTG2,
+ .pgc = BIT(IMX8M_PGC_OTG2),
},
[IMX8M_POWER_DOMAIN_DDR1] = {
@@ -492,7 +500,7 @@ static const struct imx_pgc_domain imx8m_pgc_domains[] = {
.pxx = IMX8M_DDR1_SW_Pxx_REQ,
.map = IMX8M_DDR2_A53_DOMAIN,
},
- .pgc = IMX8M_PGC_DDR1,
+ .pgc = BIT(IMX8M_PGC_DDR1),
},
[IMX8M_POWER_DOMAIN_GPU] = {
@@ -505,7 +513,7 @@ static const struct imx_pgc_domain imx8m_pgc_domains[] = {
.hskreq = IMX8M_GPU_HSK_PWRDNREQN,
.hskack = IMX8M_GPU_HSK_PWRDNACKN,
},
- .pgc = IMX8M_PGC_GPU,
+ .pgc = BIT(IMX8M_PGC_GPU),
},
[IMX8M_POWER_DOMAIN_VPU] = {
@@ -518,7 +526,8 @@ static const struct imx_pgc_domain imx8m_pgc_domains[] = {
.hskreq = IMX8M_VPU_HSK_PWRDNREQN,
.hskack = IMX8M_VPU_HSK_PWRDNACKN,
},
- .pgc = IMX8M_PGC_VPU,
+ .pgc = BIT(IMX8M_PGC_VPU),
+ .keep_clocks = true,
},
[IMX8M_POWER_DOMAIN_DISP] = {
@@ -531,7 +540,7 @@ static const struct imx_pgc_domain imx8m_pgc_domains[] = {
.hskreq = IMX8M_DISP_HSK_PWRDNREQN,
.hskack = IMX8M_DISP_HSK_PWRDNACKN,
},
- .pgc = IMX8M_PGC_DISP,
+ .pgc = BIT(IMX8M_PGC_DISP),
},
[IMX8M_POWER_DOMAIN_MIPI_CSI1] = {
@@ -542,7 +551,7 @@ static const struct imx_pgc_domain imx8m_pgc_domains[] = {
.pxx = IMX8M_MIPI_CSI1_SW_Pxx_REQ,
.map = IMX8M_MIPI_CSI1_A53_DOMAIN,
},
- .pgc = IMX8M_PGC_MIPI_CSI1,
+ .pgc = BIT(IMX8M_PGC_MIPI_CSI1),
},
[IMX8M_POWER_DOMAIN_MIPI_CSI2] = {
@@ -553,7 +562,7 @@ static const struct imx_pgc_domain imx8m_pgc_domains[] = {
.pxx = IMX8M_MIPI_CSI2_SW_Pxx_REQ,
.map = IMX8M_MIPI_CSI2_A53_DOMAIN,
},
- .pgc = IMX8M_PGC_MIPI_CSI2,
+ .pgc = BIT(IMX8M_PGC_MIPI_CSI2),
},
[IMX8M_POWER_DOMAIN_PCIE2] = {
@@ -564,7 +573,7 @@ static const struct imx_pgc_domain imx8m_pgc_domains[] = {
.pxx = IMX8M_PCIE2_SW_Pxx_REQ,
.map = IMX8M_PCIE2_A53_DOMAIN,
},
- .pgc = IMX8M_PGC_PCIE2,
+ .pgc = BIT(IMX8M_PGC_PCIE2),
},
};
@@ -617,6 +626,7 @@ static const struct imx_pgc_domain imx8mm_pgc_domains[] = {
.hskreq = IMX8MM_HSIO_HSK_PWRDNREQN,
.hskack = IMX8MM_HSIO_HSK_PWRDNACKN,
},
+ .keep_clocks = true,
},
[IMX8MM_POWER_DOMAIN_PCIE] = {
@@ -627,7 +637,7 @@ static const struct imx_pgc_domain imx8mm_pgc_domains[] = {
.pxx = IMX8MM_PCIE_SW_Pxx_REQ,
.map = IMX8MM_PCIE_A53_DOMAIN,
},
- .pgc = IMX8MM_PGC_PCIE,
+ .pgc = BIT(IMX8MM_PGC_PCIE),
},
[IMX8MM_POWER_DOMAIN_OTG1] = {
@@ -638,7 +648,7 @@ static const struct imx_pgc_domain imx8mm_pgc_domains[] = {
.pxx = IMX8MM_OTG1_SW_Pxx_REQ,
.map = IMX8MM_OTG1_A53_DOMAIN,
},
- .pgc = IMX8MM_PGC_OTG1,
+ .pgc = BIT(IMX8MM_PGC_OTG1),
},
[IMX8MM_POWER_DOMAIN_OTG2] = {
@@ -649,7 +659,7 @@ static const struct imx_pgc_domain imx8mm_pgc_domains[] = {
.pxx = IMX8MM_OTG2_SW_Pxx_REQ,
.map = IMX8MM_OTG2_A53_DOMAIN,
},
- .pgc = IMX8MM_PGC_OTG2,
+ .pgc = BIT(IMX8MM_PGC_OTG2),
},
[IMX8MM_POWER_DOMAIN_GPUMIX] = {
@@ -662,7 +672,8 @@ static const struct imx_pgc_domain imx8mm_pgc_domains[] = {
.hskreq = IMX8MM_GPUMIX_HSK_PWRDNREQN,
.hskack = IMX8MM_GPUMIX_HSK_PWRDNACKN,
},
- .pgc = IMX8MM_PGC_GPUMIX,
+ .pgc = BIT(IMX8MM_PGC_GPUMIX),
+ .keep_clocks = true,
},
[IMX8MM_POWER_DOMAIN_GPU] = {
@@ -675,7 +686,7 @@ static const struct imx_pgc_domain imx8mm_pgc_domains[] = {
.hskreq = IMX8MM_GPU_HSK_PWRDNREQN,
.hskack = IMX8MM_GPU_HSK_PWRDNACKN,
},
- .pgc = IMX8MM_PGC_GPU2D,
+ .pgc = BIT(IMX8MM_PGC_GPU2D) | BIT(IMX8MM_PGC_GPU3D),
},
[IMX8MM_POWER_DOMAIN_VPUMIX] = {
@@ -688,7 +699,8 @@ static const struct imx_pgc_domain imx8mm_pgc_domains[] = {
.hskreq = IMX8MM_VPUMIX_HSK_PWRDNREQN,
.hskack = IMX8MM_VPUMIX_HSK_PWRDNACKN,
},
- .pgc = IMX8MM_PGC_VPUMIX,
+ .pgc = BIT(IMX8MM_PGC_VPUMIX),
+ .keep_clocks = true,
},
[IMX8MM_POWER_DOMAIN_VPUG1] = {
@@ -699,7 +711,7 @@ static const struct imx_pgc_domain imx8mm_pgc_domains[] = {
.pxx = IMX8MM_VPUG1_SW_Pxx_REQ,
.map = IMX8MM_VPUG1_A53_DOMAIN,
},
- .pgc = IMX8MM_PGC_VPUG1,
+ .pgc = BIT(IMX8MM_PGC_VPUG1),
},
[IMX8MM_POWER_DOMAIN_VPUG2] = {
@@ -710,7 +722,7 @@ static const struct imx_pgc_domain imx8mm_pgc_domains[] = {
.pxx = IMX8MM_VPUG2_SW_Pxx_REQ,
.map = IMX8MM_VPUG2_A53_DOMAIN,
},
- .pgc = IMX8MM_PGC_VPUG2,
+ .pgc = BIT(IMX8MM_PGC_VPUG2),
},
[IMX8MM_POWER_DOMAIN_VPUH1] = {
@@ -721,7 +733,7 @@ static const struct imx_pgc_domain imx8mm_pgc_domains[] = {
.pxx = IMX8MM_VPUH1_SW_Pxx_REQ,
.map = IMX8MM_VPUH1_A53_DOMAIN,
},
- .pgc = IMX8MM_PGC_VPUH1,
+ .pgc = BIT(IMX8MM_PGC_VPUH1),
},
[IMX8MM_POWER_DOMAIN_DISPMIX] = {
@@ -734,7 +746,8 @@ static const struct imx_pgc_domain imx8mm_pgc_domains[] = {
.hskreq = IMX8MM_DISPMIX_HSK_PWRDNREQN,
.hskack = IMX8MM_DISPMIX_HSK_PWRDNACKN,
},
- .pgc = IMX8MM_PGC_DISPMIX,
+ .pgc = BIT(IMX8MM_PGC_DISPMIX),
+ .keep_clocks = true,
},
[IMX8MM_POWER_DOMAIN_MIPI] = {
@@ -745,7 +758,7 @@ static const struct imx_pgc_domain imx8mm_pgc_domains[] = {
.pxx = IMX8MM_MIPI_SW_Pxx_REQ,
.map = IMX8MM_MIPI_A53_DOMAIN,
},
- .pgc = IMX8MM_PGC_MIPI,
+ .pgc = BIT(IMX8MM_PGC_MIPI),
},
};
@@ -802,6 +815,7 @@ static const struct imx_pgc_domain imx8mn_pgc_domains[] = {
.hskreq = IMX8MN_HSIO_HSK_PWRDNREQN,
.hskack = IMX8MN_HSIO_HSK_PWRDNACKN,
},
+ .keep_clocks = true,
},
[IMX8MN_POWER_DOMAIN_OTG1] = {
@@ -812,7 +826,7 @@ static const struct imx_pgc_domain imx8mn_pgc_domains[] = {
.pxx = IMX8MN_OTG1_SW_Pxx_REQ,
.map = IMX8MN_OTG1_A53_DOMAIN,
},
- .pgc = IMX8MN_PGC_OTG1,
+ .pgc = BIT(IMX8MN_PGC_OTG1),
},
[IMX8MN_POWER_DOMAIN_GPUMIX] = {
@@ -825,7 +839,7 @@ static const struct imx_pgc_domain imx8mn_pgc_domains[] = {
.hskreq = IMX8MN_GPUMIX_HSK_PWRDNREQN,
.hskack = IMX8MN_GPUMIX_HSK_PWRDNACKN,
},
- .pgc = IMX8MN_PGC_GPUMIX,
+ .pgc = BIT(IMX8MN_PGC_GPUMIX),
},
};
@@ -894,6 +908,10 @@ static int imx_pgc_domain_probe(struct platform_device *pdev)
goto out_domain_unmap;
}
+ if (IS_ENABLED(CONFIG_LOCKDEP) &&
+ of_property_read_bool(domain->dev->of_node, "power-domains"))
+ lockdep_set_subclass(&domain->genpd.mlock, 1);
+
ret = of_genpd_add_provider_simple(domain->dev->of_node,
&domain->genpd);
if (ret) {
@@ -930,6 +948,36 @@ static int imx_pgc_domain_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+static int imx_pgc_domain_suspend(struct device *dev)
+{
+ int ret;
+
+ /*
+ * This may look strange, but is done so the generic PM_SLEEP code
+ * can power down our domain and more importantly power it up again
+ * after resume, without tripping over our usage of runtime PM to
+ * power up/down the nested domains.
+ */
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ pm_runtime_put_noidle(dev);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int imx_pgc_domain_resume(struct device *dev)
+{
+ return pm_runtime_put(dev);
+}
+#endif
+
+static const struct dev_pm_ops imx_pgc_domain_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(imx_pgc_domain_suspend, imx_pgc_domain_resume)
+};
+
static const struct platform_device_id imx_pgc_domain_id[] = {
{ "imx-pgc-domain", },
{ },
@@ -938,6 +986,7 @@ static const struct platform_device_id imx_pgc_domain_id[] = {
static struct platform_driver imx_pgc_domain_driver = {
.driver = {
.name = "imx-pgc",
+ .pm = &imx_pgc_domain_pm_ops,
},
.probe = imx_pgc_domain_probe,
.remove = imx_pgc_domain_remove,
@@ -986,6 +1035,9 @@ static int imx_gpcv2_probe(struct platform_device *pdev)
struct imx_pgc_domain *domain;
u32 domain_index;
+ if (!of_device_is_available(np))
+ continue;
+
ret = of_property_read_u32(np, "reg", &domain_index);
if (ret) {
dev_err(dev, "Failed to read 'reg' property\n");
diff --git a/drivers/soc/imx/imx8m-blk-ctrl.c b/drivers/soc/imx/imx8m-blk-ctrl.c
new file mode 100644
index 000000000000..519b3651d1d9
--- /dev/null
+++ b/drivers/soc/imx/imx8m-blk-ctrl.c
@@ -0,0 +1,523 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/*
+ * Copyright 2021 Pengutronix, Lucas Stach <kernel@pengutronix.de>
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/clk.h>
+
+#include <dt-bindings/power/imx8mm-power.h>
+
+#define BLK_SFT_RSTN 0x0
+#define BLK_CLK_EN 0x4
+
+struct imx8m_blk_ctrl_domain;
+
+struct imx8m_blk_ctrl {
+ struct device *dev;
+ struct notifier_block power_nb;
+ struct device *bus_power_dev;
+ struct regmap *regmap;
+ struct imx8m_blk_ctrl_domain *domains;
+ struct genpd_onecell_data onecell_data;
+};
+
+struct imx8m_blk_ctrl_domain_data {
+ const char *name;
+ const char * const *clk_names;
+ int num_clks;
+ const char *gpc_name;
+ u32 rst_mask;
+ u32 clk_mask;
+};
+
+#define DOMAIN_MAX_CLKS 3
+
+struct imx8m_blk_ctrl_domain {
+ struct generic_pm_domain genpd;
+ const struct imx8m_blk_ctrl_domain_data *data;
+ struct clk_bulk_data clks[DOMAIN_MAX_CLKS];
+ struct device *power_dev;
+ struct imx8m_blk_ctrl *bc;
+};
+
+struct imx8m_blk_ctrl_data {
+ int max_reg;
+ notifier_fn_t power_notifier_fn;
+ const struct imx8m_blk_ctrl_domain_data *domains;
+ int num_domains;
+};
+
+static inline struct imx8m_blk_ctrl_domain *
+to_imx8m_blk_ctrl_domain(struct generic_pm_domain *genpd)
+{
+ return container_of(genpd, struct imx8m_blk_ctrl_domain, genpd);
+}
+
+static int imx8m_blk_ctrl_power_on(struct generic_pm_domain *genpd)
+{
+ struct imx8m_blk_ctrl_domain *domain = to_imx8m_blk_ctrl_domain(genpd);
+ const struct imx8m_blk_ctrl_domain_data *data = domain->data;
+ struct imx8m_blk_ctrl *bc = domain->bc;
+ int ret;
+
+ /* make sure bus domain is awake */
+ ret = pm_runtime_get_sync(bc->bus_power_dev);
+ if (ret < 0) {
+ pm_runtime_put_noidle(bc->bus_power_dev);
+ dev_err(bc->dev, "failed to power up bus domain\n");
+ return ret;
+ }
+
+ /* put devices into reset */
+ regmap_clear_bits(bc->regmap, BLK_SFT_RSTN, data->rst_mask);
+
+ /* enable upstream and blk-ctrl clocks to allow reset to propagate */
+ ret = clk_bulk_prepare_enable(data->num_clks, domain->clks);
+ if (ret) {
+ dev_err(bc->dev, "failed to enable clocks\n");
+ goto bus_put;
+ }
+ regmap_set_bits(bc->regmap, BLK_CLK_EN, data->clk_mask);
+
+ /* power up upstream GPC domain */
+ ret = pm_runtime_get_sync(domain->power_dev);
+ if (ret < 0) {
+ dev_err(bc->dev, "failed to power up peripheral domain\n");
+ goto clk_disable;
+ }
+
+ /* wait for reset to propagate */
+ udelay(5);
+
+ /* release reset */
+ regmap_set_bits(bc->regmap, BLK_SFT_RSTN, data->rst_mask);
+
+ /* disable upstream clocks */
+ clk_bulk_disable_unprepare(data->num_clks, domain->clks);
+
+ return 0;
+
+clk_disable:
+ clk_bulk_disable_unprepare(data->num_clks, domain->clks);
+bus_put:
+ pm_runtime_put(bc->bus_power_dev);
+
+ return ret;
+}
+
+static int imx8m_blk_ctrl_power_off(struct generic_pm_domain *genpd)
+{
+ struct imx8m_blk_ctrl_domain *domain = to_imx8m_blk_ctrl_domain(genpd);
+ const struct imx8m_blk_ctrl_domain_data *data = domain->data;
+ struct imx8m_blk_ctrl *bc = domain->bc;
+
+ /* put devices into reset and disable clocks */
+ regmap_clear_bits(bc->regmap, BLK_SFT_RSTN, data->rst_mask);
+ regmap_clear_bits(bc->regmap, BLK_CLK_EN, data->clk_mask);
+
+ /* power down upstream GPC domain */
+ pm_runtime_put(domain->power_dev);
+
+ /* allow bus domain to suspend */
+ pm_runtime_put(bc->bus_power_dev);
+
+ return 0;
+}
+
+static struct generic_pm_domain *
+imx8m_blk_ctrl_xlate(struct of_phandle_args *args, void *data)
+{
+ struct genpd_onecell_data *onecell_data = data;
+ unsigned int index = args->args[0];
+
+ if (args->args_count != 1 ||
+ index >= onecell_data->num_domains)
+ return ERR_PTR(-EINVAL);
+
+ return onecell_data->domains[index];
+}
+
+static struct lock_class_key blk_ctrl_genpd_lock_class;
+
+static int imx8m_blk_ctrl_probe(struct platform_device *pdev)
+{
+ const struct imx8m_blk_ctrl_data *bc_data;
+ struct device *dev = &pdev->dev;
+ struct imx8m_blk_ctrl *bc;
+ void __iomem *base;
+ int i, ret;
+
+ struct regmap_config regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ };
+
+ bc = devm_kzalloc(dev, sizeof(*bc), GFP_KERNEL);
+ if (!bc)
+ return -ENOMEM;
+
+ bc->dev = dev;
+
+ bc_data = of_device_get_match_data(dev);
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ regmap_config.max_register = bc_data->max_reg;
+ bc->regmap = devm_regmap_init_mmio(dev, base, &regmap_config);
+ if (IS_ERR(bc->regmap))
+ return dev_err_probe(dev, PTR_ERR(bc->regmap),
+ "failed to init regmap\n");
+
+ bc->domains = devm_kcalloc(dev, bc_data->num_domains,
+ sizeof(struct imx8m_blk_ctrl_domain),
+ GFP_KERNEL);
+ if (!bc->domains)
+ return -ENOMEM;
+
+ bc->onecell_data.num_domains = bc_data->num_domains;
+ bc->onecell_data.xlate = imx8m_blk_ctrl_xlate;
+ bc->onecell_data.domains =
+ devm_kcalloc(dev, bc_data->num_domains,
+ sizeof(struct generic_pm_domain *), GFP_KERNEL);
+ if (!bc->onecell_data.domains)
+ return -ENOMEM;
+
+ bc->bus_power_dev = genpd_dev_pm_attach_by_name(dev, "bus");
+ if (IS_ERR(bc->bus_power_dev))
+ return dev_err_probe(dev, PTR_ERR(bc->bus_power_dev),
+ "failed to attach power domain\n");
+
+ for (i = 0; i < bc_data->num_domains; i++) {
+ const struct imx8m_blk_ctrl_domain_data *data = &bc_data->domains[i];
+ struct imx8m_blk_ctrl_domain *domain = &bc->domains[i];
+ int j;
+
+ domain->data = data;
+
+ for (j = 0; j < data->num_clks; j++)
+ domain->clks[j].id = data->clk_names[j];
+
+ ret = devm_clk_bulk_get(dev, data->num_clks, domain->clks);
+ if (ret) {
+ dev_err_probe(dev, ret, "failed to get clock\n");
+ goto cleanup_pds;
+ }
+
+ domain->power_dev =
+ dev_pm_domain_attach_by_name(dev, data->gpc_name);
+ if (IS_ERR(domain->power_dev)) {
+ dev_err_probe(dev, PTR_ERR(domain->power_dev),
+ "failed to attach power domain\n");
+ ret = PTR_ERR(domain->power_dev);
+ goto cleanup_pds;
+ }
+
+ domain->genpd.name = data->name;
+ domain->genpd.power_on = imx8m_blk_ctrl_power_on;
+ domain->genpd.power_off = imx8m_blk_ctrl_power_off;
+ domain->bc = bc;
+
+ ret = pm_genpd_init(&domain->genpd, NULL, true);
+ if (ret) {
+ dev_err_probe(dev, ret, "failed to init power domain\n");
+ dev_pm_domain_detach(domain->power_dev, true);
+ goto cleanup_pds;
+ }
+
+ /*
+ * We use runtime PM to trigger power on/off of the upstream GPC
+ * domain, as a strict hierarchical parent/child power domain
+ * setup doesn't allow us to meet the sequencing requirements.
+ * This means we have nested locking of genpd locks, without the
+ * nesting being visible at the genpd level, so we need a
+ * separate lock class to make lockdep aware of the fact that
+ * this are separate domain locks that can be nested without a
+ * self-deadlock.
+ */
+ lockdep_set_class(&domain->genpd.mlock,
+ &blk_ctrl_genpd_lock_class);
+
+ bc->onecell_data.domains[i] = &domain->genpd;
+ }
+
+ ret = of_genpd_add_provider_onecell(dev->of_node, &bc->onecell_data);
+ if (ret) {
+ dev_err_probe(dev, ret, "failed to add power domain provider\n");
+ goto cleanup_pds;
+ }
+
+ bc->power_nb.notifier_call = bc_data->power_notifier_fn;
+ ret = dev_pm_genpd_add_notifier(bc->bus_power_dev, &bc->power_nb);
+ if (ret) {
+ dev_err_probe(dev, ret, "failed to add power notifier\n");
+ goto cleanup_provider;
+ }
+
+ dev_set_drvdata(dev, bc);
+
+ return 0;
+
+cleanup_provider:
+ of_genpd_del_provider(dev->of_node);
+cleanup_pds:
+ for (i--; i >= 0; i--) {
+ pm_genpd_remove(&bc->domains[i].genpd);
+ dev_pm_domain_detach(bc->domains[i].power_dev, true);
+ }
+
+ dev_pm_domain_detach(bc->bus_power_dev, true);
+
+ return ret;
+}
+
+static int imx8m_blk_ctrl_remove(struct platform_device *pdev)
+{
+ struct imx8m_blk_ctrl *bc = dev_get_drvdata(&pdev->dev);
+ int i;
+
+ of_genpd_del_provider(pdev->dev.of_node);
+
+ for (i = 0; bc->onecell_data.num_domains; i++) {
+ struct imx8m_blk_ctrl_domain *domain = &bc->domains[i];
+
+ pm_genpd_remove(&domain->genpd);
+ dev_pm_domain_detach(domain->power_dev, true);
+ }
+
+ dev_pm_genpd_remove_notifier(bc->bus_power_dev);
+
+ dev_pm_domain_detach(bc->bus_power_dev, true);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int imx8m_blk_ctrl_suspend(struct device *dev)
+{
+ struct imx8m_blk_ctrl *bc = dev_get_drvdata(dev);
+ int ret, i;
+
+ /*
+ * This may look strange, but is done so the generic PM_SLEEP code
+ * can power down our domains and more importantly power them up again
+ * after resume, without tripping over our usage of runtime PM to
+ * control the upstream GPC domains. Things happen in the right order
+ * in the system suspend/resume paths due to the device parent/child
+ * hierarchy.
+ */
+ ret = pm_runtime_get_sync(bc->bus_power_dev);
+ if (ret < 0) {
+ pm_runtime_put_noidle(bc->bus_power_dev);
+ return ret;
+ }
+
+ for (i = 0; i < bc->onecell_data.num_domains; i++) {
+ struct imx8m_blk_ctrl_domain *domain = &bc->domains[i];
+
+ ret = pm_runtime_get_sync(domain->power_dev);
+ if (ret < 0) {
+ pm_runtime_put_noidle(domain->power_dev);
+ goto out_fail;
+ }
+ }
+
+ return 0;
+
+out_fail:
+ for (i--; i >= 0; i--)
+ pm_runtime_put(bc->domains[i].power_dev);
+
+ pm_runtime_put(bc->bus_power_dev);
+
+ return ret;
+}
+
+static int imx8m_blk_ctrl_resume(struct device *dev)
+{
+ struct imx8m_blk_ctrl *bc = dev_get_drvdata(dev);
+ int i;
+
+ for (i = 0; i < bc->onecell_data.num_domains; i++)
+ pm_runtime_put(bc->domains[i].power_dev);
+
+ pm_runtime_put(bc->bus_power_dev);
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops imx8m_blk_ctrl_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(imx8m_blk_ctrl_suspend, imx8m_blk_ctrl_resume)
+};
+
+static int imx8mm_vpu_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;
+
+ /*
+ * The ADB in the VPUMIX domain has no separate reset and clock
+ * enable bits, but is ungated together with the VPU clocks. To
+ * allow the handshake with the GPC to progress we put the VPUs
+ * in reset and ungate the clocks.
+ */
+ regmap_clear_bits(bc->regmap, BLK_SFT_RSTN, BIT(0) | BIT(1) | BIT(2));
+ regmap_set_bits(bc->regmap, BLK_CLK_EN, BIT(0) | BIT(1) | BIT(2));
+
+ if (action == GENPD_NOTIFY_ON) {
+ /*
+ * 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.
+ */
+ udelay(5);
+
+ /* set "fuse" bits to enable the VPUs */
+ regmap_set_bits(bc->regmap, 0x8, 0xffffffff);
+ regmap_set_bits(bc->regmap, 0xc, 0xffffffff);
+ regmap_set_bits(bc->regmap, 0x10, 0xffffffff);
+ regmap_set_bits(bc->regmap, 0x14, 0xffffffff);
+ }
+
+ return NOTIFY_OK;
+}
+
+static const struct imx8m_blk_ctrl_domain_data imx8mm_vpu_blk_ctl_domain_data[] = {
+ [IMX8MM_VPUBLK_PD_G1] = {
+ .name = "vpublk-g1",
+ .clk_names = (const char *[]){ "g1", },
+ .num_clks = 1,
+ .gpc_name = "g1",
+ .rst_mask = BIT(1),
+ .clk_mask = BIT(1),
+ },
+ [IMX8MM_VPUBLK_PD_G2] = {
+ .name = "vpublk-g2",
+ .clk_names = (const char *[]){ "g2", },
+ .num_clks = 1,
+ .gpc_name = "g2",
+ .rst_mask = BIT(0),
+ .clk_mask = BIT(0),
+ },
+ [IMX8MM_VPUBLK_PD_H1] = {
+ .name = "vpublk-h1",
+ .clk_names = (const char *[]){ "h1", },
+ .num_clks = 1,
+ .gpc_name = "h1",
+ .rst_mask = BIT(2),
+ .clk_mask = BIT(2),
+ },
+};
+
+static const struct imx8m_blk_ctrl_data imx8mm_vpu_blk_ctl_dev_data = {
+ .max_reg = 0x18,
+ .power_notifier_fn = imx8mm_vpu_power_notifier,
+ .domains = imx8mm_vpu_blk_ctl_domain_data,
+ .num_domains = ARRAY_SIZE(imx8mm_vpu_blk_ctl_domain_data),
+};
+
+static int imx8mm_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(12));
+ regmap_set_bits(bc->regmap, BLK_SFT_RSTN, BIT(6));
+
+ /*
+ * 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 imx8mm_disp_blk_ctl_domain_data[] = {
+ [IMX8MM_DISPBLK_PD_CSI_BRIDGE] = {
+ .name = "dispblk-csi-bridge",
+ .clk_names = (const char *[]){ "csi-bridge-axi", "csi-bridge-apb",
+ "csi-bridge-core", },
+ .num_clks = 3,
+ .gpc_name = "csi-bridge",
+ .rst_mask = BIT(0) | BIT(1) | BIT(2),
+ .clk_mask = BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5),
+ },
+ [IMX8MM_DISPBLK_PD_LCDIF] = {
+ .name = "dispblk-lcdif",
+ .clk_names = (const char *[]){ "lcdif-axi", "lcdif-apb", "lcdif-pix", },
+ .num_clks = 3,
+ .gpc_name = "lcdif",
+ .clk_mask = BIT(6) | BIT(7),
+ },
+ [IMX8MM_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(5),
+ .clk_mask = BIT(8) | BIT(9),
+ },
+ [IMX8MM_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(3) | BIT(4),
+ .clk_mask = BIT(10) | BIT(11),
+ },
+};
+
+static const struct imx8m_blk_ctrl_data imx8mm_disp_blk_ctl_dev_data = {
+ .max_reg = 0x2c,
+ .power_notifier_fn = imx8mm_disp_power_notifier,
+ .domains = imx8mm_disp_blk_ctl_domain_data,
+ .num_domains = ARRAY_SIZE(imx8mm_disp_blk_ctl_domain_data),
+};
+
+static const struct of_device_id imx8m_blk_ctrl_of_match[] = {
+ {
+ .compatible = "fsl,imx8mm-vpu-blk-ctrl",
+ .data = &imx8mm_vpu_blk_ctl_dev_data
+ }, {
+ .compatible = "fsl,imx8mm-disp-blk-ctrl",
+ .data = &imx8mm_disp_blk_ctl_dev_data
+ } ,{
+ /* Sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, imx8m_blk_ctrl_of_match);
+
+static struct platform_driver imx8m_blk_ctrl_driver = {
+ .probe = imx8m_blk_ctrl_probe,
+ .remove = imx8m_blk_ctrl_remove,
+ .driver = {
+ .name = "imx8m-blk-ctrl",
+ .pm = &imx8m_blk_ctrl_pm_ops,
+ .of_match_table = imx8m_blk_ctrl_of_match,
+ },
+};
+module_platform_driver(imx8m_blk_ctrl_driver);
diff --git a/drivers/soc/mediatek/mt8192-mmsys.h b/drivers/soc/mediatek/mt8192-mmsys.h
new file mode 100644
index 000000000000..6f0a57044a7b
--- /dev/null
+++ b/drivers/soc/mediatek/mt8192-mmsys.h
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef __SOC_MEDIATEK_MT8192_MMSYS_H
+#define __SOC_MEDIATEK_MT8192_MMSYS_H
+
+#define MT8192_MMSYS_OVL_MOUT_EN 0xf04
+#define MT8192_DISP_OVL1_2L_MOUT_EN 0xf08
+#define MT8192_DISP_OVL0_2L_MOUT_EN 0xf18
+#define MT8192_DISP_OVL0_MOUT_EN 0xf1c
+#define MT8192_DISP_RDMA0_SEL_IN 0xf2c
+#define MT8192_DISP_RDMA0_SOUT_SEL 0xf30
+#define MT8192_DISP_CCORR0_SOUT_SEL 0xf34
+#define MT8192_DISP_AAL0_SEL_IN 0xf38
+#define MT8192_DISP_DITHER0_MOUT_EN 0xf3c
+#define MT8192_DISP_DSI0_SEL_IN 0xf40
+#define MT8192_DISP_OVL2_2L_MOUT_EN 0xf4c
+
+#define MT8192_DISP_OVL0_GO_BLEND BIT(0)
+#define MT8192_DITHER0_MOUT_IN_DSI0 BIT(0)
+#define MT8192_OVL0_MOUT_EN_DISP_RDMA0 BIT(0)
+#define MT8192_OVL2_2L_MOUT_EN_RDMA4 BIT(0)
+#define MT8192_DISP_OVL0_GO_BG BIT(1)
+#define MT8192_DISP_OVL0_2L_GO_BLEND BIT(2)
+#define MT8192_DISP_OVL0_2L_GO_BG BIT(3)
+#define MT8192_OVL1_2L_MOUT_EN_RDMA1 BIT(4)
+#define MT8192_OVL0_MOUT_EN_OVL0_2L BIT(4)
+#define MT8192_RDMA0_SEL_IN_OVL0_2L 0x3
+#define MT8192_RDMA0_SOUT_COLOR0 0x1
+#define MT8192_CCORR0_SOUT_AAL0 0x1
+#define MT8192_AAL0_SEL_IN_CCORR0 0x1
+#define MT8192_DSI0_SEL_IN_DITHER0 0x1
+
+static const struct mtk_mmsys_routes mmsys_mt8192_routing_table[] = {
+ {
+ DDP_COMPONENT_OVL_2L0, DDP_COMPONENT_RDMA0,
+ MT8192_DISP_OVL0_2L_MOUT_EN, MT8192_OVL0_MOUT_EN_DISP_RDMA0,
+ MT8192_OVL0_MOUT_EN_DISP_RDMA0
+ }, {
+ DDP_COMPONENT_OVL_2L2, DDP_COMPONENT_RDMA4,
+ MT8192_DISP_OVL2_2L_MOUT_EN, MT8192_OVL2_2L_MOUT_EN_RDMA4,
+ MT8192_OVL2_2L_MOUT_EN_RDMA4
+ }, {
+ DDP_COMPONENT_DITHER, DDP_COMPONENT_DSI0,
+ MT8192_DISP_DITHER0_MOUT_EN, MT8192_DITHER0_MOUT_IN_DSI0,
+ MT8192_DITHER0_MOUT_IN_DSI0
+ }, {
+ DDP_COMPONENT_OVL_2L0, DDP_COMPONENT_RDMA0,
+ MT8192_DISP_RDMA0_SEL_IN, MT8192_RDMA0_SEL_IN_OVL0_2L,
+ MT8192_RDMA0_SEL_IN_OVL0_2L
+ }, {
+ DDP_COMPONENT_CCORR, DDP_COMPONENT_AAL0,
+ MT8192_DISP_AAL0_SEL_IN, MT8192_AAL0_SEL_IN_CCORR0,
+ MT8192_AAL0_SEL_IN_CCORR0
+ }, {
+ DDP_COMPONENT_DITHER, DDP_COMPONENT_DSI0,
+ MT8192_DISP_DSI0_SEL_IN, MT8192_DSI0_SEL_IN_DITHER0
+ }, {
+ DDP_COMPONENT_RDMA0, DDP_COMPONENT_COLOR0,
+ MT8192_DISP_RDMA0_SOUT_SEL, MT8192_RDMA0_SOUT_COLOR0,
+ MT8192_RDMA0_SOUT_COLOR0
+ }, {
+ DDP_COMPONENT_CCORR, DDP_COMPONENT_AAL0,
+ MT8192_DISP_CCORR0_SOUT_SEL, MT8192_CCORR0_SOUT_AAL0,
+ MT8192_CCORR0_SOUT_AAL0
+ }, {
+ DDP_COMPONENT_OVL0, DDP_COMPONENT_OVL_2L0,
+ MT8192_MMSYS_OVL_MOUT_EN, MT8192_DISP_OVL0_GO_BG,
+ MT8192_DISP_OVL0_GO_BG
+ }, {
+ DDP_COMPONENT_OVL_2L0, DDP_COMPONENT_RDMA0,
+ MT8192_MMSYS_OVL_MOUT_EN, MT8192_DISP_OVL0_2L_GO_BLEND,
+ MT8192_DISP_OVL0_2L_GO_BLEND
+ }
+};
+
+#endif /* __SOC_MEDIATEK_MT8192_MMSYS_H */
diff --git a/drivers/soc/mediatek/mtk-mmsys.c b/drivers/soc/mediatek/mtk-mmsys.c
index a78e88f27b62..1e448f1ffefb 100644
--- a/drivers/soc/mediatek/mtk-mmsys.c
+++ b/drivers/soc/mediatek/mtk-mmsys.c
@@ -4,15 +4,18 @@
* Author: James Liao <jamesjj.liao@mediatek.com>
*/
+#include <linux/delay.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
+#include <linux/reset-controller.h>
#include <linux/soc/mediatek/mtk-mmsys.h>
#include "mtk-mmsys.h"
#include "mt8167-mmsys.h"
#include "mt8183-mmsys.h"
+#include "mt8192-mmsys.h"
#include "mt8365-mmsys.h"
static const struct mtk_mmsys_driver_data mt2701_mmsys_driver_data = {
@@ -53,6 +56,12 @@ static const struct mtk_mmsys_driver_data mt8183_mmsys_driver_data = {
.num_routes = ARRAY_SIZE(mmsys_mt8183_routing_table),
};
+static const struct mtk_mmsys_driver_data mt8192_mmsys_driver_data = {
+ .clk_driver = "clk-mt8192-mm",
+ .routes = mmsys_mt8192_routing_table,
+ .num_routes = ARRAY_SIZE(mmsys_mt8192_routing_table),
+};
+
static const struct mtk_mmsys_driver_data mt8365_mmsys_driver_data = {
.clk_driver = "clk-mt8365-mm",
.routes = mt8365_mmsys_routing_table,
@@ -62,6 +71,8 @@ static const struct mtk_mmsys_driver_data mt8365_mmsys_driver_data = {
struct mtk_mmsys {
void __iomem *regs;
const struct mtk_mmsys_driver_data *data;
+ spinlock_t lock; /* protects mmsys_sw_rst_b reg */
+ struct reset_controller_dev rcdev;
};
void mtk_mmsys_ddp_connect(struct device *dev,
@@ -101,6 +112,58 @@ void mtk_mmsys_ddp_disconnect(struct device *dev,
}
EXPORT_SYMBOL_GPL(mtk_mmsys_ddp_disconnect);
+static int mtk_mmsys_reset_update(struct reset_controller_dev *rcdev, unsigned long id,
+ bool assert)
+{
+ struct mtk_mmsys *mmsys = container_of(rcdev, struct mtk_mmsys, rcdev);
+ unsigned long flags;
+ u32 reg;
+
+ spin_lock_irqsave(&mmsys->lock, flags);
+
+ reg = readl_relaxed(mmsys->regs + MMSYS_SW0_RST_B);
+
+ if (assert)
+ reg &= ~BIT(id);
+ else
+ reg |= BIT(id);
+
+ writel_relaxed(reg, mmsys->regs + MMSYS_SW0_RST_B);
+
+ spin_unlock_irqrestore(&mmsys->lock, flags);
+
+ return 0;
+}
+
+static int mtk_mmsys_reset_assert(struct reset_controller_dev *rcdev, unsigned long id)
+{
+ return mtk_mmsys_reset_update(rcdev, id, true);
+}
+
+static int mtk_mmsys_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id)
+{
+ return mtk_mmsys_reset_update(rcdev, id, false);
+}
+
+static int mtk_mmsys_reset(struct reset_controller_dev *rcdev, unsigned long id)
+{
+ int ret;
+
+ ret = mtk_mmsys_reset_assert(rcdev, id);
+ if (ret)
+ return ret;
+
+ usleep_range(1000, 1100);
+
+ return mtk_mmsys_reset_deassert(rcdev, id);
+}
+
+static const struct reset_control_ops mtk_mmsys_reset_ops = {
+ .assert = mtk_mmsys_reset_assert,
+ .deassert = mtk_mmsys_reset_deassert,
+ .reset = mtk_mmsys_reset,
+};
+
static int mtk_mmsys_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -120,6 +183,18 @@ static int mtk_mmsys_probe(struct platform_device *pdev)
return ret;
}
+ spin_lock_init(&mmsys->lock);
+
+ mmsys->rcdev.owner = THIS_MODULE;
+ mmsys->rcdev.nr_resets = 32;
+ mmsys->rcdev.ops = &mtk_mmsys_reset_ops;
+ mmsys->rcdev.of_node = pdev->dev.of_node;
+ ret = devm_reset_controller_register(&pdev->dev, &mmsys->rcdev);
+ if (ret) {
+ dev_err(&pdev->dev, "Couldn't register mmsys reset controller: %d\n", ret);
+ return ret;
+ }
+
mmsys->data = of_device_get_match_data(&pdev->dev);
platform_set_drvdata(pdev, mmsys);
@@ -168,6 +243,10 @@ static const struct of_device_id of_match_mtk_mmsys[] = {
.data = &mt8183_mmsys_driver_data,
},
{
+ .compatible = "mediatek,mt8192-mmsys",
+ .data = &mt8192_mmsys_driver_data,
+ },
+ {
.compatible = "mediatek,mt8365-mmsys",
.data = &mt8365_mmsys_driver_data,
},
diff --git a/drivers/soc/mediatek/mtk-mmsys.h b/drivers/soc/mediatek/mtk-mmsys.h
index 9e2b81bd38db..8b0ed05117ea 100644
--- a/drivers/soc/mediatek/mtk-mmsys.h
+++ b/drivers/soc/mediatek/mtk-mmsys.h
@@ -78,6 +78,8 @@
#define DSI_SEL_IN_RDMA 0x1
#define DSI_SEL_IN_MASK 0x1
+#define MMSYS_SW0_RST_B 0x140
+
struct mtk_mmsys_routes {
u32 from_comp;
u32 to_comp;
diff --git a/drivers/soc/mediatek/mtk-mutex.c b/drivers/soc/mediatek/mtk-mutex.c
index 2e4bcc300576..2ca55bb5a8be 100644
--- a/drivers/soc/mediatek/mtk-mutex.c
+++ b/drivers/soc/mediatek/mtk-mutex.c
@@ -39,6 +39,18 @@
#define MT8167_MUTEX_MOD_DISP_DITHER 15
#define MT8167_MUTEX_MOD_DISP_UFOE 16
+#define MT8192_MUTEX_MOD_DISP_OVL0 0
+#define MT8192_MUTEX_MOD_DISP_OVL0_2L 1
+#define MT8192_MUTEX_MOD_DISP_RDMA0 2
+#define MT8192_MUTEX_MOD_DISP_COLOR0 4
+#define MT8192_MUTEX_MOD_DISP_CCORR0 5
+#define MT8192_MUTEX_MOD_DISP_AAL0 6
+#define MT8192_MUTEX_MOD_DISP_GAMMA0 7
+#define MT8192_MUTEX_MOD_DISP_POSTMASK0 8
+#define MT8192_MUTEX_MOD_DISP_DITHER0 9
+#define MT8192_MUTEX_MOD_DISP_OVL2_2L 16
+#define MT8192_MUTEX_MOD_DISP_RDMA4 17
+
#define MT8183_MUTEX_MOD_DISP_RDMA0 0
#define MT8183_MUTEX_MOD_DISP_RDMA1 1
#define MT8183_MUTEX_MOD_DISP_OVL0 9
@@ -214,6 +226,20 @@ static const unsigned int mt8183_mutex_mod[DDP_COMPONENT_ID_MAX] = {
[DDP_COMPONENT_WDMA0] = MT8183_MUTEX_MOD_DISP_WDMA0,
};
+static const unsigned int mt8192_mutex_mod[DDP_COMPONENT_ID_MAX] = {
+ [DDP_COMPONENT_AAL0] = MT8192_MUTEX_MOD_DISP_AAL0,
+ [DDP_COMPONENT_CCORR] = MT8192_MUTEX_MOD_DISP_CCORR0,
+ [DDP_COMPONENT_COLOR0] = MT8192_MUTEX_MOD_DISP_COLOR0,
+ [DDP_COMPONENT_DITHER] = MT8192_MUTEX_MOD_DISP_DITHER0,
+ [DDP_COMPONENT_GAMMA] = MT8192_MUTEX_MOD_DISP_GAMMA0,
+ [DDP_COMPONENT_POSTMASK0] = MT8192_MUTEX_MOD_DISP_POSTMASK0,
+ [DDP_COMPONENT_OVL0] = MT8192_MUTEX_MOD_DISP_OVL0,
+ [DDP_COMPONENT_OVL_2L0] = MT8192_MUTEX_MOD_DISP_OVL0_2L,
+ [DDP_COMPONENT_OVL_2L2] = MT8192_MUTEX_MOD_DISP_OVL2_2L,
+ [DDP_COMPONENT_RDMA0] = MT8192_MUTEX_MOD_DISP_RDMA0,
+ [DDP_COMPONENT_RDMA4] = MT8192_MUTEX_MOD_DISP_RDMA4,
+};
+
static const unsigned int mt2712_mutex_sof[MUTEX_SOF_DSI3 + 1] = {
[MUTEX_SOF_SINGLE_MODE] = MUTEX_SOF_SINGLE_MODE,
[MUTEX_SOF_DSI0] = MUTEX_SOF_DSI0,
@@ -275,6 +301,13 @@ static const struct mtk_mutex_data mt8183_mutex_driver_data = {
.no_clk = true,
};
+static const struct mtk_mutex_data mt8192_mutex_driver_data = {
+ .mutex_mod = mt8192_mutex_mod,
+ .mutex_sof = mt8183_mutex_sof,
+ .mutex_mod_reg = MT8183_MUTEX0_MOD0,
+ .mutex_sof_reg = MT8183_MUTEX0_SOF0,
+};
+
struct mtk_mutex *mtk_mutex_get(struct device *dev)
{
struct mtk_mutex_ctx *mtx = dev_get_drvdata(dev);
@@ -507,6 +540,8 @@ static const struct of_device_id mutex_driver_dt_match[] = {
.data = &mt8173_mutex_driver_data},
{ .compatible = "mediatek,mt8183-disp-mutex",
.data = &mt8183_mutex_driver_data},
+ { .compatible = "mediatek,mt8192-disp-mutex",
+ .data = &mt8192_mutex_driver_data},
{},
};
MODULE_DEVICE_TABLE(of, mutex_driver_dt_match);
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index 79b568f82a1c..e718b8735444 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -190,6 +190,25 @@ config QCOM_SOCINFO
Say yes here to support the Qualcomm socinfo driver, providing
information about the SoC to user space.
+config QCOM_SPM
+ tristate "Qualcomm Subsystem Power Manager (SPM)"
+ depends on ARCH_QCOM || COMPILE_TEST
+ select QCOM_SCM
+ help
+ Enable the support for the Qualcomm Subsystem Power Manager, used
+ to manage cores, L2 low power modes and to configure the internal
+ Adaptive Voltage Scaler parameters, where supported.
+
+config QCOM_STATS
+ tristate "Qualcomm Technologies, Inc. (QTI) Sleep stats driver"
+ depends on (ARCH_QCOM && DEBUG_FS) || COMPILE_TEST
+ depends on QCOM_SMEM
+ help
+ Qualcomm Technologies, Inc. (QTI) Sleep stats driver to read
+ the shared memory exported by the remote processor related to
+ various SoC level low power modes statistics and export to debugfs
+ interface.
+
config QCOM_WCNSS_CTRL
tristate "Qualcomm WCNSS control driver"
depends on ARCH_QCOM || COMPILE_TEST
@@ -199,7 +218,7 @@ config QCOM_WCNSS_CTRL
firmware to a newly booted WCNSS chip.
config QCOM_APR
- tristate "Qualcomm APR Bus (Asynchronous Packet Router)"
+ tristate "Qualcomm APR/GPR Bus (Asynchronous/Generic Packet Router)"
depends on ARCH_QCOM || COMPILE_TEST
depends on RPMSG
depends on NET
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index ad675a6593d0..70d5de69fd7b 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -20,6 +20,8 @@ obj-$(CONFIG_QCOM_SMEM_STATE) += smem_state.o
obj-$(CONFIG_QCOM_SMP2P) += smp2p.o
obj-$(CONFIG_QCOM_SMSM) += smsm.o
obj-$(CONFIG_QCOM_SOCINFO) += socinfo.o
+obj-$(CONFIG_QCOM_SPM) += spm.o
+obj-$(CONFIG_QCOM_STATS) += qcom_stats.o
obj-$(CONFIG_QCOM_WCNSS_CTRL) += wcnss_ctrl.o
obj-$(CONFIG_QCOM_APR) += apr.o
obj-$(CONFIG_QCOM_LLCC) += llcc-qcom.o
diff --git a/drivers/soc/qcom/apr.c b/drivers/soc/qcom/apr.c
index 475a57b435b2..82ca12c9328a 100644
--- a/drivers/soc/qcom/apr.c
+++ b/drivers/soc/qcom/apr.c
@@ -15,13 +15,23 @@
#include <linux/rpmsg.h>
#include <linux/of.h>
-struct apr {
+enum {
+ PR_TYPE_APR = 0,
+ PR_TYPE_GPR,
+};
+
+/* Some random values tbh which does not collide with static modules */
+#define GPR_DYNAMIC_PORT_START 0x10000000
+#define GPR_DYNAMIC_PORT_END 0x20000000
+
+struct packet_router {
struct rpmsg_endpoint *ch;
struct device *dev;
spinlock_t svcs_lock;
spinlock_t rx_lock;
struct idr svcs_idr;
int dest_domain_id;
+ int type;
struct pdr_handle *pdr;
struct workqueue_struct *rxwq;
struct work_struct rx_work;
@@ -44,26 +54,103 @@ struct apr_rx_buf {
*/
int apr_send_pkt(struct apr_device *adev, struct apr_pkt *pkt)
{
- struct apr *apr = dev_get_drvdata(adev->dev.parent);
+ struct packet_router *apr = dev_get_drvdata(adev->dev.parent);
struct apr_hdr *hdr;
unsigned long flags;
int ret;
- spin_lock_irqsave(&adev->lock, flags);
+ spin_lock_irqsave(&adev->svc.lock, flags);
hdr = &pkt->hdr;
hdr->src_domain = APR_DOMAIN_APPS;
- hdr->src_svc = adev->svc_id;
+ hdr->src_svc = adev->svc.id;
hdr->dest_domain = adev->domain_id;
- hdr->dest_svc = adev->svc_id;
+ hdr->dest_svc = adev->svc.id;
ret = rpmsg_trysend(apr->ch, pkt, hdr->pkt_size);
- spin_unlock_irqrestore(&adev->lock, flags);
+ spin_unlock_irqrestore(&adev->svc.lock, flags);
return ret ? ret : hdr->pkt_size;
}
EXPORT_SYMBOL_GPL(apr_send_pkt);
+void gpr_free_port(gpr_port_t *port)
+{
+ struct packet_router *gpr = port->pr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&gpr->svcs_lock, flags);
+ idr_remove(&gpr->svcs_idr, port->id);
+ spin_unlock_irqrestore(&gpr->svcs_lock, flags);
+
+ kfree(port);
+}
+EXPORT_SYMBOL_GPL(gpr_free_port);
+
+gpr_port_t *gpr_alloc_port(struct apr_device *gdev, struct device *dev,
+ gpr_port_cb cb, void *priv)
+{
+ struct packet_router *pr = dev_get_drvdata(gdev->dev.parent);
+ gpr_port_t *port;
+ struct pkt_router_svc *svc;
+ int id;
+
+ port = kzalloc(sizeof(*port), GFP_KERNEL);
+ if (!port)
+ return ERR_PTR(-ENOMEM);
+
+ svc = port;
+ svc->callback = cb;
+ svc->pr = pr;
+ svc->priv = priv;
+ svc->dev = dev;
+ spin_lock_init(&svc->lock);
+
+ spin_lock(&pr->svcs_lock);
+ id = idr_alloc_cyclic(&pr->svcs_idr, svc, GPR_DYNAMIC_PORT_START,
+ GPR_DYNAMIC_PORT_END, GFP_ATOMIC);
+ if (id < 0) {
+ dev_err(dev, "Unable to allocate dynamic GPR src port\n");
+ kfree(port);
+ spin_unlock(&pr->svcs_lock);
+ return ERR_PTR(id);
+ }
+
+ svc->id = id;
+ spin_unlock(&pr->svcs_lock);
+
+ return port;
+}
+EXPORT_SYMBOL_GPL(gpr_alloc_port);
+
+static int pkt_router_send_svc_pkt(struct pkt_router_svc *svc, struct gpr_pkt *pkt)
+{
+ struct packet_router *pr = svc->pr;
+ struct gpr_hdr *hdr;
+ unsigned long flags;
+ int ret;
+
+ hdr = &pkt->hdr;
+
+ spin_lock_irqsave(&svc->lock, flags);
+ ret = rpmsg_trysend(pr->ch, pkt, hdr->pkt_size);
+ spin_unlock_irqrestore(&svc->lock, flags);
+
+ return ret ? ret : hdr->pkt_size;
+}
+
+int gpr_send_pkt(struct apr_device *gdev, struct gpr_pkt *pkt)
+{
+ return pkt_router_send_svc_pkt(&gdev->svc, pkt);
+}
+EXPORT_SYMBOL_GPL(gpr_send_pkt);
+
+int gpr_send_port_pkt(gpr_port_t *port, struct gpr_pkt *pkt)
+{
+ return pkt_router_send_svc_pkt(port, pkt);
+}
+EXPORT_SYMBOL_GPL(gpr_send_port_pkt);
+
static void apr_dev_release(struct device *dev)
{
struct apr_device *adev = to_apr_device(dev);
@@ -74,7 +161,7 @@ static void apr_dev_release(struct device *dev)
static int apr_callback(struct rpmsg_device *rpdev, void *buf,
int len, void *priv, u32 addr)
{
- struct apr *apr = dev_get_drvdata(&rpdev->dev);
+ struct packet_router *apr = dev_get_drvdata(&rpdev->dev);
struct apr_rx_buf *abuf;
unsigned long flags;
@@ -100,11 +187,11 @@ static int apr_callback(struct rpmsg_device *rpdev, void *buf,
return 0;
}
-
-static int apr_do_rx_callback(struct apr *apr, struct apr_rx_buf *abuf)
+static int apr_do_rx_callback(struct packet_router *apr, struct apr_rx_buf *abuf)
{
uint16_t hdr_size, msg_type, ver, svc_id;
- struct apr_device *svc = NULL;
+ struct pkt_router_svc *svc;
+ struct apr_device *adev;
struct apr_driver *adrv = NULL;
struct apr_resp_pkt resp;
struct apr_hdr *hdr;
@@ -145,12 +232,15 @@ static int apr_do_rx_callback(struct apr *apr, struct apr_rx_buf *abuf)
svc_id = hdr->dest_svc;
spin_lock_irqsave(&apr->svcs_lock, flags);
svc = idr_find(&apr->svcs_idr, svc_id);
- if (svc && svc->dev.driver)
- adrv = to_apr_driver(svc->dev.driver);
+ if (svc && svc->dev->driver) {
+ adev = svc_to_apr_device(svc);
+ adrv = to_apr_driver(adev->dev.driver);
+ }
spin_unlock_irqrestore(&apr->svcs_lock, flags);
- if (!adrv) {
- dev_err(apr->dev, "APR: service is not registered\n");
+ if (!adrv || !adev) {
+ dev_err(apr->dev, "APR: service is not registered (%d)\n",
+ svc_id);
return -EINVAL;
}
@@ -164,20 +254,82 @@ static int apr_do_rx_callback(struct apr *apr, struct apr_rx_buf *abuf)
if (resp.payload_size > 0)
resp.payload = buf + hdr_size;
- adrv->callback(svc, &resp);
+ adrv->callback(adev, &resp);
+
+ return 0;
+}
+
+static int gpr_do_rx_callback(struct packet_router *gpr, struct apr_rx_buf *abuf)
+{
+ uint16_t hdr_size, ver;
+ struct pkt_router_svc *svc = NULL;
+ struct gpr_resp_pkt resp;
+ struct gpr_hdr *hdr;
+ unsigned long flags;
+ void *buf = abuf->buf;
+ int len = abuf->len;
+
+ hdr = buf;
+ ver = hdr->version;
+ if (ver > GPR_PKT_VER + 1)
+ return -EINVAL;
+
+ hdr_size = hdr->hdr_size;
+ if (hdr_size < GPR_PKT_HEADER_WORD_SIZE) {
+ dev_err(gpr->dev, "GPR: Wrong hdr size:%d\n", hdr_size);
+ return -EINVAL;
+ }
+
+ if (hdr->pkt_size < GPR_PKT_HEADER_BYTE_SIZE || hdr->pkt_size != len) {
+ dev_err(gpr->dev, "GPR: Wrong packet size\n");
+ return -EINVAL;
+ }
+
+ resp.hdr = *hdr;
+ resp.payload_size = hdr->pkt_size - (hdr_size * 4);
+
+ /*
+ * NOTE: hdr_size is not same as GPR_HDR_SIZE as remote can include
+ * optional headers in to gpr_hdr which should be ignored
+ */
+ if (resp.payload_size > 0)
+ resp.payload = buf + (hdr_size * 4);
+
+
+ spin_lock_irqsave(&gpr->svcs_lock, flags);
+ svc = idr_find(&gpr->svcs_idr, hdr->dest_port);
+ spin_unlock_irqrestore(&gpr->svcs_lock, flags);
+
+ if (!svc) {
+ dev_err(gpr->dev, "GPR: Port(%x) is not registered\n",
+ hdr->dest_port);
+ return -EINVAL;
+ }
+
+ if (svc->callback)
+ svc->callback(&resp, svc->priv, 0);
return 0;
}
static void apr_rxwq(struct work_struct *work)
{
- struct apr *apr = container_of(work, struct apr, rx_work);
+ struct packet_router *apr = container_of(work, struct packet_router, rx_work);
struct apr_rx_buf *abuf, *b;
unsigned long flags;
if (!list_empty(&apr->rx_list)) {
list_for_each_entry_safe(abuf, b, &apr->rx_list, node) {
- apr_do_rx_callback(apr, abuf);
+ switch (apr->type) {
+ case PR_TYPE_APR:
+ apr_do_rx_callback(apr, abuf);
+ break;
+ case PR_TYPE_GPR:
+ gpr_do_rx_callback(apr, abuf);
+ break;
+ default:
+ break;
+ }
spin_lock_irqsave(&apr->rx_lock, flags);
list_del(&abuf->node);
spin_unlock_irqrestore(&apr->rx_lock, flags);
@@ -201,7 +353,7 @@ static int apr_device_match(struct device *dev, struct device_driver *drv)
while (id->domain_id != 0 || id->svc_id != 0) {
if (id->domain_id == adev->domain_id &&
- id->svc_id == adev->svc_id)
+ id->svc_id == adev->svc.id)
return 1;
id++;
}
@@ -213,22 +365,27 @@ static int apr_device_probe(struct device *dev)
{
struct apr_device *adev = to_apr_device(dev);
struct apr_driver *adrv = to_apr_driver(dev->driver);
+ int ret;
- return adrv->probe(adev);
+ ret = adrv->probe(adev);
+ if (!ret)
+ adev->svc.callback = adrv->gpr_callback;
+
+ return ret;
}
static void apr_device_remove(struct device *dev)
{
struct apr_device *adev = to_apr_device(dev);
struct apr_driver *adrv;
- struct apr *apr = dev_get_drvdata(adev->dev.parent);
+ struct packet_router *apr = dev_get_drvdata(adev->dev.parent);
if (dev->driver) {
adrv = to_apr_driver(dev->driver);
if (adrv->remove)
adrv->remove(adev);
spin_lock(&apr->svcs_lock);
- idr_remove(&apr->svcs_idr, adev->svc_id);
+ idr_remove(&apr->svcs_idr, adev->svc.id);
spin_unlock(&apr->svcs_lock);
}
}
@@ -255,28 +412,43 @@ struct bus_type aprbus = {
EXPORT_SYMBOL_GPL(aprbus);
static int apr_add_device(struct device *dev, struct device_node *np,
- const struct apr_device_id *id)
+ u32 svc_id, u32 domain_id)
{
- struct apr *apr = dev_get_drvdata(dev);
+ struct packet_router *apr = dev_get_drvdata(dev);
struct apr_device *adev = NULL;
+ struct pkt_router_svc *svc;
int ret;
adev = kzalloc(sizeof(*adev), GFP_KERNEL);
if (!adev)
return -ENOMEM;
- spin_lock_init(&adev->lock);
+ adev->svc_id = svc_id;
+ svc = &adev->svc;
+
+ svc->id = svc_id;
+ svc->pr = apr;
+ svc->priv = adev;
+ svc->dev = dev;
+ spin_lock_init(&svc->lock);
+
+ adev->domain_id = domain_id;
- adev->svc_id = id->svc_id;
- adev->domain_id = id->domain_id;
- adev->version = id->svc_version;
if (np)
snprintf(adev->name, APR_NAME_SIZE, "%pOFn", np);
- else
- strscpy(adev->name, id->name, APR_NAME_SIZE);
- dev_set_name(&adev->dev, "aprsvc:%s:%x:%x", adev->name,
- id->domain_id, id->svc_id);
+ switch (apr->type) {
+ case PR_TYPE_APR:
+ dev_set_name(&adev->dev, "aprsvc:%s:%x:%x", adev->name,
+ domain_id, svc_id);
+ break;
+ case PR_TYPE_GPR:
+ dev_set_name(&adev->dev, "gprsvc:%s:%x:%x", adev->name,
+ domain_id, svc_id);
+ break;
+ default:
+ break;
+ }
adev->dev.bus = &aprbus;
adev->dev.parent = dev;
@@ -285,14 +457,13 @@ static int apr_add_device(struct device *dev, struct device_node *np,
adev->dev.driver = NULL;
spin_lock(&apr->svcs_lock);
- idr_alloc(&apr->svcs_idr, adev, id->svc_id,
- id->svc_id + 1, GFP_ATOMIC);
+ idr_alloc(&apr->svcs_idr, svc, svc_id, svc_id + 1, GFP_ATOMIC);
spin_unlock(&apr->svcs_lock);
of_property_read_string_index(np, "qcom,protection-domain",
1, &adev->service_path);
- dev_info(dev, "Adding APR dev: %s\n", dev_name(&adev->dev));
+ dev_info(dev, "Adding APR/GPR dev: %s\n", dev_name(&adev->dev));
ret = device_register(&adev->dev);
if (ret) {
@@ -306,7 +477,7 @@ static int apr_add_device(struct device *dev, struct device_node *np,
static int of_apr_add_pd_lookups(struct device *dev)
{
const char *service_name, *service_path;
- struct apr *apr = dev_get_drvdata(dev);
+ struct packet_router *apr = dev_get_drvdata(dev);
struct device_node *node;
struct pdr_service *pds;
int ret;
@@ -321,12 +492,14 @@ static int of_apr_add_pd_lookups(struct device *dev)
1, &service_path);
if (ret < 0) {
dev_err(dev, "pdr service path missing: %d\n", ret);
+ of_node_put(node);
return ret;
}
pds = pdr_add_lookup(apr->pdr, service_name, service_path);
if (IS_ERR(pds) && PTR_ERR(pds) != -EALREADY) {
dev_err(dev, "pdr add lookup failed: %ld\n", PTR_ERR(pds));
+ of_node_put(node);
return PTR_ERR(pds);
}
}
@@ -336,13 +509,14 @@ static int of_apr_add_pd_lookups(struct device *dev)
static void of_register_apr_devices(struct device *dev, const char *svc_path)
{
- struct apr *apr = dev_get_drvdata(dev);
+ struct packet_router *apr = dev_get_drvdata(dev);
struct device_node *node;
const char *service_path;
int ret;
for_each_child_of_node(dev->of_node, node) {
- struct apr_device_id id = { {0} };
+ u32 svc_id;
+ u32 domain_id;
/*
* This function is called with svc_path NULL during
@@ -372,13 +546,13 @@ static void of_register_apr_devices(struct device *dev, const char *svc_path)
continue;
}
- if (of_property_read_u32(node, "reg", &id.svc_id))
+ if (of_property_read_u32(node, "reg", &svc_id))
continue;
- id.domain_id = apr->dest_domain_id;
+ domain_id = apr->dest_domain_id;
- if (apr_add_device(dev, node, &id))
- dev_err(dev, "Failed to add apr %d svc\n", id.svc_id);
+ if (apr_add_device(dev, node, svc_id, domain_id))
+ dev_err(dev, "Failed to add apr %d svc\n", svc_id);
}
}
@@ -398,7 +572,7 @@ static int apr_remove_device(struct device *dev, void *svc_path)
static void apr_pd_status(int state, char *svc_path, void *priv)
{
- struct apr *apr = (struct apr *)priv;
+ struct packet_router *apr = (struct packet_router *)priv;
switch (state) {
case SERVREG_SERVICE_STATE_UP:
@@ -413,16 +587,26 @@ static void apr_pd_status(int state, char *svc_path, void *priv)
static int apr_probe(struct rpmsg_device *rpdev)
{
struct device *dev = &rpdev->dev;
- struct apr *apr;
+ struct packet_router *apr;
int ret;
apr = devm_kzalloc(dev, sizeof(*apr), GFP_KERNEL);
if (!apr)
return -ENOMEM;
- ret = of_property_read_u32(dev->of_node, "qcom,apr-domain", &apr->dest_domain_id);
+ ret = of_property_read_u32(dev->of_node, "qcom,domain", &apr->dest_domain_id);
+
+ if (of_device_is_compatible(dev->of_node, "qcom,gpr")) {
+ apr->type = PR_TYPE_GPR;
+ } else {
+ if (ret) /* try deprecated apr-domain property */
+ ret = of_property_read_u32(dev->of_node, "qcom,apr-domain",
+ &apr->dest_domain_id);
+ apr->type = PR_TYPE_APR;
+ }
+
if (ret) {
- dev_err(dev, "APR Domain ID not specified in DT\n");
+ dev_err(dev, "Domain ID not specified in DT\n");
return ret;
}
@@ -465,7 +649,7 @@ destroy_wq:
static void apr_remove(struct rpmsg_device *rpdev)
{
- struct apr *apr = dev_get_drvdata(&rpdev->dev);
+ struct packet_router *apr = dev_get_drvdata(&rpdev->dev);
pdr_handle_release(apr->pdr);
device_for_each_child(&rpdev->dev, NULL, apr_remove_device);
@@ -502,20 +686,21 @@ void apr_driver_unregister(struct apr_driver *drv)
}
EXPORT_SYMBOL_GPL(apr_driver_unregister);
-static const struct of_device_id apr_of_match[] = {
+static const struct of_device_id pkt_router_of_match[] = {
{ .compatible = "qcom,apr"},
{ .compatible = "qcom,apr-v2"},
+ { .compatible = "qcom,gpr"},
{}
};
-MODULE_DEVICE_TABLE(of, apr_of_match);
+MODULE_DEVICE_TABLE(of, pkt_router_of_match);
-static struct rpmsg_driver apr_driver = {
+static struct rpmsg_driver packet_router_driver = {
.probe = apr_probe,
.remove = apr_remove,
.callback = apr_callback,
.drv = {
.name = "qcom,apr",
- .of_match_table = apr_of_match,
+ .of_match_table = pkt_router_of_match,
},
};
@@ -525,7 +710,7 @@ static int __init apr_init(void)
ret = bus_register(&aprbus);
if (!ret)
- ret = register_rpmsg_driver(&apr_driver);
+ ret = register_rpmsg_driver(&packet_router_driver);
else
bus_unregister(&aprbus);
@@ -535,7 +720,7 @@ static int __init apr_init(void)
static void __exit apr_exit(void)
{
bus_unregister(&aprbus);
- unregister_rpmsg_driver(&apr_driver);
+ unregister_rpmsg_driver(&packet_router_driver);
}
subsys_initcall(apr_init);
diff --git a/drivers/soc/qcom/cpr.c b/drivers/soc/qcom/cpr.c
index 4ce8e816154f..1d818a8ba208 100644
--- a/drivers/soc/qcom/cpr.c
+++ b/drivers/soc/qcom/cpr.c
@@ -1614,7 +1614,6 @@ static void cpr_debugfs_init(struct cpr_drv *drv)
static int cpr_probe(struct platform_device *pdev)
{
- struct resource *res;
struct device *dev = &pdev->dev;
struct cpr_drv *drv;
int irq, ret;
@@ -1648,8 +1647,7 @@ static int cpr_probe(struct platform_device *pdev)
if (IS_ERR(drv->tcsr))
return PTR_ERR(drv->tcsr);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- drv->base = devm_ioremap_resource(dev, res);
+ drv->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(drv->base))
return PTR_ERR(drv->base);
diff --git a/drivers/soc/qcom/llcc-qcom.c b/drivers/soc/qcom/llcc-qcom.c
index 15a36dcab990..6bf2f1d1f2c5 100644
--- a/drivers/soc/qcom/llcc-qcom.c
+++ b/drivers/soc/qcom/llcc-qcom.c
@@ -115,7 +115,7 @@ static const struct llcc_slice_config sc7280_data[] = {
{ LLCC_CMPT, 10, 768, 1, 1, 0x3f, 0x0, 0, 0, 0, 1, 0, 0},
{ LLCC_GPUHTW, 11, 256, 1, 1, 0x3f, 0x0, 0, 0, 0, 1, 0, 0},
{ LLCC_GPU, 12, 512, 1, 0, 0x3f, 0x0, 0, 0, 0, 1, 0, 0},
- { LLCC_MMUHWT, 13, 256, 1, 1, 0x3f, 0x0, 0, 0, 0, 1, 1, 0},
+ { LLCC_MMUHWT, 13, 256, 1, 1, 0x3f, 0x0, 0, 0, 0, 0, 1, 0},
{ LLCC_MDMPNG, 21, 768, 0, 1, 0x3f, 0x0, 0, 0, 0, 1, 0, 0},
{ LLCC_WLHW, 24, 256, 1, 1, 0x3f, 0x0, 0, 0, 0, 1, 0, 0},
{ LLCC_MODPE, 29, 64, 1, 1, 0x3f, 0x0, 0, 0, 0, 1, 0, 0},
@@ -142,6 +142,16 @@ static const struct llcc_slice_config sdm845_data[] = {
{ LLCC_AUDHW, 22, 1024, 1, 1, 0xffc, 0x2, 0, 0, 1, 1, 0 },
};
+static const struct llcc_slice_config sm6350_data[] = {
+ { LLCC_CPUSS, 1, 768, 1, 0, 0xFFF, 0x0, 0, 0, 0, 0, 1, 1 },
+ { LLCC_MDM, 8, 512, 2, 0, 0xFFF, 0x0, 0, 0, 0, 0, 1, 0 },
+ { LLCC_GPUHTW, 11, 256, 1, 0, 0xFFF, 0x0, 0, 0, 0, 0, 1, 0 },
+ { LLCC_GPU, 12, 512, 1, 0, 0xFFF, 0x0, 0, 0, 0, 0, 1, 0 },
+ { LLCC_MDMPNG, 21, 768, 0, 1, 0xFFF, 0x0, 0, 0, 0, 0, 1, 0 },
+ { LLCC_NPU, 23, 768, 1, 0, 0xFFF, 0x0, 0, 0, 0, 0, 1, 0 },
+ { LLCC_MODPE, 29, 64, 1, 1, 0xFFF, 0x0, 0, 0, 0, 0, 1, 0 },
+};
+
static const struct llcc_slice_config sm8150_data[] = {
{ LLCC_CPUSS, 1, 3072, 1, 1, 0xFFF, 0x0, 0, 0, 0, 1, 1 },
{ LLCC_VIDSC0, 2, 512, 2, 1, 0xFFF, 0x0, 0, 0, 0, 1, 0 },
@@ -203,6 +213,11 @@ static const struct qcom_llcc_config sdm845_cfg = {
.need_llcc_cfg = false,
};
+static const struct qcom_llcc_config sm6350_cfg = {
+ .sct_data = sm6350_data,
+ .size = ARRAY_SIZE(sm6350_data),
+};
+
static const struct qcom_llcc_config sm8150_cfg = {
.sct_data = sm8150_data,
.size = ARRAY_SIZE(sm8150_data),
@@ -626,6 +641,7 @@ static const struct of_device_id qcom_llcc_of_match[] = {
{ .compatible = "qcom,sc7180-llcc", .data = &sc7180_cfg },
{ .compatible = "qcom,sc7280-llcc", .data = &sc7280_cfg },
{ .compatible = "qcom,sdm845-llcc", .data = &sdm845_cfg },
+ { .compatible = "qcom,sm6350-llcc", .data = &sm6350_cfg },
{ .compatible = "qcom,sm8150-llcc", .data = &sm8150_cfg },
{ .compatible = "qcom,sm8250-llcc", .data = &sm8250_cfg },
{ }
diff --git a/drivers/soc/qcom/ocmem.c b/drivers/soc/qcom/ocmem.c
index f1875dc31ae2..d2dacbbaafbd 100644
--- a/drivers/soc/qcom/ocmem.c
+++ b/drivers/soc/qcom/ocmem.c
@@ -300,7 +300,6 @@ static int ocmem_dev_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
unsigned long reg, region_size;
int i, j, ret, num_banks;
- struct resource *res;
struct ocmem *ocmem;
if (!qcom_scm_is_available())
@@ -321,8 +320,7 @@ static int ocmem_dev_probe(struct platform_device *pdev)
return ret;
}
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctrl");
- ocmem->mmio = devm_ioremap_resource(&pdev->dev, res);
+ ocmem->mmio = devm_platform_ioremap_resource_byname(pdev, "ctrl");
if (IS_ERR(ocmem->mmio)) {
dev_err(&pdev->dev, "Failed to ioremap ocmem_ctrl resource\n");
return PTR_ERR(ocmem->mmio);
diff --git a/drivers/soc/qcom/pdr_interface.c b/drivers/soc/qcom/pdr_interface.c
index 915d5bc3d46e..fc580a3c4336 100644
--- a/drivers/soc/qcom/pdr_interface.c
+++ b/drivers/soc/qcom/pdr_interface.c
@@ -131,7 +131,7 @@ static int pdr_register_listener(struct pdr_handle *pdr,
return ret;
req.enable = enable;
- strcpy(req.service_path, pds->service_path);
+ strscpy(req.service_path, pds->service_path, sizeof(req.service_path));
ret = qmi_send_request(&pdr->notifier_hdl, &pds->addr,
&txn, SERVREG_REGISTER_LISTENER_REQ,
@@ -257,7 +257,7 @@ static int pdr_send_indack_msg(struct pdr_handle *pdr, struct pdr_service *pds,
return ret;
req.transaction_id = tid;
- strcpy(req.service_path, pds->service_path);
+ strscpy(req.service_path, pds->service_path, sizeof(req.service_path));
ret = qmi_send_request(&pdr->notifier_hdl, &pds->addr,
&txn, SERVREG_SET_ACK_REQ,
@@ -406,7 +406,7 @@ static int pdr_locate_service(struct pdr_handle *pdr, struct pdr_service *pds)
return -ENOMEM;
/* Prepare req message */
- strcpy(req.service_name, pds->service_name);
+ strscpy(req.service_name, pds->service_name, sizeof(req.service_name));
req.domain_offset_valid = true;
req.domain_offset = 0;
@@ -531,8 +531,8 @@ struct pdr_service *pdr_add_lookup(struct pdr_handle *pdr,
return ERR_PTR(-ENOMEM);
pds->service = SERVREG_NOTIFIER_SERVICE;
- strcpy(pds->service_name, service_name);
- strcpy(pds->service_path, service_path);
+ strscpy(pds->service_name, service_name, sizeof(pds->service_name));
+ strscpy(pds->service_path, service_path, sizeof(pds->service_path));
pds->need_locator_lookup = true;
mutex_lock(&pdr->list_lock);
@@ -587,7 +587,7 @@ int pdr_restart_pd(struct pdr_handle *pdr, struct pdr_service *pds)
break;
/* Prepare req message */
- strcpy(req.service_path, pds->service_path);
+ strscpy(req.service_path, pds->service_path, sizeof(req.service_path));
addr = pds->addr;
break;
}
diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c
index 7d649d2cf31e..28a8c0dda66c 100644
--- a/drivers/soc/qcom/qcom-geni-se.c
+++ b/drivers/soc/qcom/qcom-geni-se.c
@@ -871,7 +871,6 @@ EXPORT_SYMBOL(geni_icc_disable);
static int geni_se_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct resource *res;
struct geni_wrapper *wrapper;
int ret;
@@ -880,8 +879,7 @@ static int geni_se_probe(struct platform_device *pdev)
return -ENOMEM;
wrapper->dev = dev;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- wrapper->base = devm_ioremap_resource(dev, res);
+ wrapper->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(wrapper->base))
return PTR_ERR(wrapper->base);
diff --git a/drivers/soc/qcom/qcom_aoss.c b/drivers/soc/qcom/qcom_aoss.c
index 536c3e4114fb..34acf58bbb0d 100644
--- a/drivers/soc/qcom/qcom_aoss.c
+++ b/drivers/soc/qcom/qcom_aoss.c
@@ -2,16 +2,16 @@
/*
* Copyright (c) 2019, Linaro Ltd
*/
-#include <dt-bindings/power/qcom-aoss-qmp.h>
#include <linux/clk-provider.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/mailbox_client.h>
#include <linux/module.h>
+#include <linux/of_platform.h>
#include <linux/platform_device.h>
-#include <linux/pm_domain.h>
#include <linux/thermal.h>
#include <linux/slab.h>
+#include <linux/soc/qcom/qcom_aoss.h>
#define QMP_DESC_MAGIC 0x0
#define QMP_DESC_VERSION 0x4
@@ -64,7 +64,6 @@ struct qmp_cooling_device {
* @event: wait_queue for synchronization with the IRQ
* @tx_lock: provides synchronization between multiple callers of qmp_send()
* @qdss_clk: QDSS clock hw struct
- * @pd_data: genpd data
* @cooling_devs: thermal cooling devices
*/
struct qmp {
@@ -82,17 +81,9 @@ struct qmp {
struct mutex tx_lock;
struct clk_hw qdss_clk;
- struct genpd_onecell_data pd_data;
struct qmp_cooling_device *cooling_devs;
};
-struct qmp_pd {
- struct qmp *qmp;
- struct generic_pm_domain pd;
-};
-
-#define to_qmp_pd_resource(res) container_of(res, struct qmp_pd, pd)
-
static void qmp_kick(struct qmp *qmp)
{
mbox_send_message(qmp->mbox_chan, NULL);
@@ -223,11 +214,14 @@ static bool qmp_message_empty(struct qmp *qmp)
*
* Return: 0 on success, negative errno on failure
*/
-static int qmp_send(struct qmp *qmp, const void *data, size_t len)
+int qmp_send(struct qmp *qmp, const void *data, size_t len)
{
long time_left;
int ret;
+ if (WARN_ON(IS_ERR_OR_NULL(qmp) || !data))
+ return -EINVAL;
+
if (WARN_ON(len + sizeof(u32) > qmp->size))
return -EINVAL;
@@ -261,6 +255,7 @@ static int qmp_send(struct qmp *qmp, const void *data, size_t len)
return ret;
}
+EXPORT_SYMBOL(qmp_send);
static int qmp_qdss_clk_prepare(struct clk_hw *hw)
{
@@ -314,95 +309,6 @@ static void qmp_qdss_clk_remove(struct qmp *qmp)
clk_hw_unregister(&qmp->qdss_clk);
}
-static int qmp_pd_power_toggle(struct qmp_pd *res, bool enable)
-{
- char buf[QMP_MSG_LEN] = {};
-
- snprintf(buf, sizeof(buf),
- "{class: image, res: load_state, name: %s, val: %s}",
- res->pd.name, enable ? "on" : "off");
- return qmp_send(res->qmp, buf, sizeof(buf));
-}
-
-static int qmp_pd_power_on(struct generic_pm_domain *domain)
-{
- return qmp_pd_power_toggle(to_qmp_pd_resource(domain), true);
-}
-
-static int qmp_pd_power_off(struct generic_pm_domain *domain)
-{
- return qmp_pd_power_toggle(to_qmp_pd_resource(domain), false);
-}
-
-static const char * const sdm845_resources[] = {
- [AOSS_QMP_LS_CDSP] = "cdsp",
- [AOSS_QMP_LS_LPASS] = "adsp",
- [AOSS_QMP_LS_MODEM] = "modem",
- [AOSS_QMP_LS_SLPI] = "slpi",
- [AOSS_QMP_LS_SPSS] = "spss",
- [AOSS_QMP_LS_VENUS] = "venus",
-};
-
-static int qmp_pd_add(struct qmp *qmp)
-{
- struct genpd_onecell_data *data = &qmp->pd_data;
- struct device *dev = qmp->dev;
- struct qmp_pd *res;
- size_t num = ARRAY_SIZE(sdm845_resources);
- int ret;
- int i;
-
- res = devm_kcalloc(dev, num, sizeof(*res), GFP_KERNEL);
- if (!res)
- return -ENOMEM;
-
- data->domains = devm_kcalloc(dev, num, sizeof(*data->domains),
- GFP_KERNEL);
- if (!data->domains)
- return -ENOMEM;
-
- for (i = 0; i < num; i++) {
- res[i].qmp = qmp;
- res[i].pd.name = sdm845_resources[i];
- res[i].pd.power_on = qmp_pd_power_on;
- res[i].pd.power_off = qmp_pd_power_off;
-
- ret = pm_genpd_init(&res[i].pd, NULL, true);
- if (ret < 0) {
- dev_err(dev, "failed to init genpd\n");
- goto unroll_genpds;
- }
-
- data->domains[i] = &res[i].pd;
- }
-
- data->num_domains = i;
-
- ret = of_genpd_add_provider_onecell(dev->of_node, data);
- if (ret < 0)
- goto unroll_genpds;
-
- return 0;
-
-unroll_genpds:
- for (i--; i >= 0; i--)
- pm_genpd_remove(data->domains[i]);
-
- return ret;
-}
-
-static void qmp_pd_remove(struct qmp *qmp)
-{
- struct genpd_onecell_data *data = &qmp->pd_data;
- struct device *dev = qmp->dev;
- int i;
-
- of_genpd_del_provider(dev->of_node);
-
- for (i = 0; i < data->num_domains; i++)
- pm_genpd_remove(data->domains[i]);
-}
-
static int qmp_cdev_get_max_state(struct thermal_cooling_device *cdev,
unsigned long *state)
{
@@ -519,9 +425,53 @@ static void qmp_cooling_devices_remove(struct qmp *qmp)
thermal_cooling_device_unregister(qmp->cooling_devs[i].cdev);
}
+/**
+ * qmp_get() - get a qmp handle from a device
+ * @dev: client device pointer
+ *
+ * Return: handle to qmp device on success, ERR_PTR() on failure
+ */
+struct qmp *qmp_get(struct device *dev)
+{
+ struct platform_device *pdev;
+ struct device_node *np;
+ struct qmp *qmp;
+
+ if (!dev || !dev->of_node)
+ return ERR_PTR(-EINVAL);
+
+ np = of_parse_phandle(dev->of_node, "qcom,qmp", 0);
+ if (!np)
+ return ERR_PTR(-ENODEV);
+
+ pdev = of_find_device_by_node(np);
+ of_node_put(np);
+ if (!pdev)
+ return ERR_PTR(-EINVAL);
+
+ qmp = platform_get_drvdata(pdev);
+
+ return qmp ? qmp : ERR_PTR(-EPROBE_DEFER);
+}
+EXPORT_SYMBOL(qmp_get);
+
+/**
+ * qmp_put() - release a qmp handle
+ * @qmp: qmp handle obtained from qmp_get()
+ */
+void qmp_put(struct qmp *qmp)
+{
+ /*
+ * Match get_device() inside of_find_device_by_node() in
+ * qmp_get()
+ */
+ if (!IS_ERR_OR_NULL(qmp))
+ put_device(qmp->dev);
+}
+EXPORT_SYMBOL(qmp_put);
+
static int qmp_probe(struct platform_device *pdev)
{
- struct resource *res;
struct qmp *qmp;
int irq;
int ret;
@@ -534,8 +484,7 @@ static int qmp_probe(struct platform_device *pdev)
init_waitqueue_head(&qmp->event);
mutex_init(&qmp->tx_lock);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- qmp->msgram = devm_ioremap_resource(&pdev->dev, res);
+ qmp->msgram = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(qmp->msgram))
return PTR_ERR(qmp->msgram);
@@ -563,10 +512,6 @@ static int qmp_probe(struct platform_device *pdev)
if (ret)
goto err_close_qmp;
- ret = qmp_pd_add(qmp);
- if (ret)
- goto err_remove_qdss_clk;
-
ret = qmp_cooling_devices_register(qmp);
if (ret)
dev_err(&pdev->dev, "failed to register aoss cooling devices\n");
@@ -575,8 +520,6 @@ static int qmp_probe(struct platform_device *pdev)
return 0;
-err_remove_qdss_clk:
- qmp_qdss_clk_remove(qmp);
err_close_qmp:
qmp_close(qmp);
err_free_mbox:
@@ -590,7 +533,6 @@ static int qmp_remove(struct platform_device *pdev)
struct qmp *qmp = platform_get_drvdata(pdev);
qmp_qdss_clk_remove(qmp);
- qmp_pd_remove(qmp);
qmp_cooling_devices_remove(qmp);
qmp_close(qmp);
@@ -615,6 +557,7 @@ static struct platform_driver qmp_driver = {
.driver = {
.name = "qcom_aoss_qmp",
.of_match_table = qmp_dt_match,
+ .suppress_bind_attrs = true,
},
.probe = qmp_probe,
.remove = qmp_remove,
diff --git a/drivers/soc/qcom/qcom_gsbi.c b/drivers/soc/qcom/qcom_gsbi.c
index 304afc223a58..290bdefbf28a 100644
--- a/drivers/soc/qcom/qcom_gsbi.c
+++ b/drivers/soc/qcom/qcom_gsbi.c
@@ -127,7 +127,6 @@ static int gsbi_probe(struct platform_device *pdev)
struct device_node *node = pdev->dev.of_node;
struct device_node *tcsr_node;
const struct of_device_id *match;
- struct resource *res;
void __iomem *base;
struct gsbi_info *gsbi;
int i, ret;
@@ -139,8 +138,7 @@ static int gsbi_probe(struct platform_device *pdev)
if (!gsbi)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(&pdev->dev, res);
+ base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
diff --git a/drivers/soc/qcom/qcom_stats.c b/drivers/soc/qcom/qcom_stats.c
new file mode 100644
index 000000000000..131d24caabf8
--- /dev/null
+++ b/drivers/soc/qcom/qcom_stats.c
@@ -0,0 +1,277 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2011-2021, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/seq_file.h>
+
+#include <linux/soc/qcom/smem.h>
+#include <clocksource/arm_arch_timer.h>
+
+#define RPM_DYNAMIC_ADDR 0x14
+#define RPM_DYNAMIC_ADDR_MASK 0xFFFF
+
+#define STAT_TYPE_OFFSET 0x0
+#define COUNT_OFFSET 0x4
+#define LAST_ENTERED_AT_OFFSET 0x8
+#define LAST_EXITED_AT_OFFSET 0x10
+#define ACCUMULATED_OFFSET 0x18
+#define CLIENT_VOTES_OFFSET 0x20
+
+struct subsystem_data {
+ const char *name;
+ u32 smem_item;
+ u32 pid;
+};
+
+static const struct subsystem_data subsystems[] = {
+ { "modem", 605, 1 },
+ { "wpss", 605, 13 },
+ { "adsp", 606, 2 },
+ { "cdsp", 607, 5 },
+ { "slpi", 608, 3 },
+ { "gpu", 609, 0 },
+ { "display", 610, 0 },
+ { "adsp_island", 613, 2 },
+ { "slpi_island", 613, 3 },
+};
+
+struct stats_config {
+ size_t stats_offset;
+ size_t num_records;
+ bool appended_stats_avail;
+ bool dynamic_offset;
+ bool subsystem_stats_in_smem;
+};
+
+struct stats_data {
+ bool appended_stats_avail;
+ void __iomem *base;
+};
+
+struct sleep_stats {
+ u32 stat_type;
+ u32 count;
+ u64 last_entered_at;
+ u64 last_exited_at;
+ u64 accumulated;
+};
+
+struct appended_stats {
+ u32 client_votes;
+ u32 reserved[3];
+};
+
+static void qcom_print_stats(struct seq_file *s, const struct sleep_stats *stat)
+{
+ u64 accumulated = stat->accumulated;
+ /*
+ * If a subsystem is in sleep when reading the sleep stats adjust
+ * the accumulated sleep duration to show actual sleep time.
+ */
+ if (stat->last_entered_at > stat->last_exited_at)
+ accumulated += arch_timer_read_counter() - stat->last_entered_at;
+
+ seq_printf(s, "Count: %u\n", stat->count);
+ seq_printf(s, "Last Entered At: %llu\n", stat->last_entered_at);
+ seq_printf(s, "Last Exited At: %llu\n", stat->last_exited_at);
+ seq_printf(s, "Accumulated Duration: %llu\n", accumulated);
+}
+
+static int qcom_subsystem_sleep_stats_show(struct seq_file *s, void *unused)
+{
+ struct subsystem_data *subsystem = s->private;
+ struct sleep_stats *stat;
+
+ /* Items are allocated lazily, so lookup pointer each time */
+ stat = qcom_smem_get(subsystem->pid, subsystem->smem_item, NULL);
+ if (IS_ERR(stat))
+ return -EIO;
+
+ qcom_print_stats(s, stat);
+
+ return 0;
+}
+
+static int qcom_soc_sleep_stats_show(struct seq_file *s, void *unused)
+{
+ struct stats_data *d = s->private;
+ void __iomem *reg = d->base;
+ struct sleep_stats stat;
+
+ memcpy_fromio(&stat, reg, sizeof(stat));
+ qcom_print_stats(s, &stat);
+
+ if (d->appended_stats_avail) {
+ struct appended_stats votes;
+
+ memcpy_fromio(&votes, reg + CLIENT_VOTES_OFFSET, sizeof(votes));
+ seq_printf(s, "Client Votes: %#x\n", votes.client_votes);
+ }
+
+ return 0;
+}
+
+DEFINE_SHOW_ATTRIBUTE(qcom_soc_sleep_stats);
+DEFINE_SHOW_ATTRIBUTE(qcom_subsystem_sleep_stats);
+
+static void qcom_create_soc_sleep_stat_files(struct dentry *root, void __iomem *reg,
+ struct stats_data *d,
+ const struct stats_config *config)
+{
+ char stat_type[sizeof(u32) + 1] = {0};
+ size_t stats_offset = config->stats_offset;
+ u32 offset = 0, type;
+ int i, j;
+
+ /*
+ * On RPM targets, stats offset location is dynamic and changes from target
+ * to target and sometimes from build to build for same target.
+ *
+ * In such cases the dynamic address is present at 0x14 offset from base
+ * address in devicetree. The last 16bits indicates the stats_offset.
+ */
+ if (config->dynamic_offset) {
+ stats_offset = readl(reg + RPM_DYNAMIC_ADDR);
+ stats_offset &= RPM_DYNAMIC_ADDR_MASK;
+ }
+
+ for (i = 0; i < config->num_records; i++) {
+ d[i].base = reg + offset + stats_offset;
+
+ /*
+ * Read the low power mode name and create debugfs file for it.
+ * The names read could be of below,
+ * (may change depending on low power mode supported).
+ * For rpmh-sleep-stats: "aosd", "cxsd" and "ddr".
+ * For rpm-sleep-stats: "vmin" and "vlow".
+ */
+ type = readl(d[i].base);
+ for (j = 0; j < sizeof(u32); j++) {
+ stat_type[j] = type & 0xff;
+ type = type >> 8;
+ }
+ strim(stat_type);
+ debugfs_create_file(stat_type, 0400, root, &d[i],
+ &qcom_soc_sleep_stats_fops);
+
+ offset += sizeof(struct sleep_stats);
+ if (d[i].appended_stats_avail)
+ offset += sizeof(struct appended_stats);
+ }
+}
+
+static void qcom_create_subsystem_stat_files(struct dentry *root,
+ const struct stats_config *config)
+{
+ const struct sleep_stats *stat;
+ int i;
+
+ if (!config->subsystem_stats_in_smem)
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(subsystems); i++) {
+ stat = qcom_smem_get(subsystems[i].pid, subsystems[i].smem_item, NULL);
+ if (IS_ERR(stat))
+ continue;
+
+ debugfs_create_file(subsystems[i].name, 0400, root, (void *)&subsystems[i],
+ &qcom_subsystem_sleep_stats_fops);
+ }
+}
+
+static int qcom_stats_probe(struct platform_device *pdev)
+{
+ void __iomem *reg;
+ struct dentry *root;
+ const struct stats_config *config;
+ struct stats_data *d;
+ int i;
+
+ config = device_get_match_data(&pdev->dev);
+ if (!config)
+ return -ENODEV;
+
+ reg = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
+ if (IS_ERR(reg))
+ return -ENOMEM;
+
+ d = devm_kcalloc(&pdev->dev, config->num_records,
+ sizeof(*d), GFP_KERNEL);
+ if (!d)
+ return -ENOMEM;
+
+ for (i = 0; i < config->num_records; i++)
+ d[i].appended_stats_avail = config->appended_stats_avail;
+
+ root = debugfs_create_dir("qcom_stats", NULL);
+
+ qcom_create_subsystem_stat_files(root, config);
+ qcom_create_soc_sleep_stat_files(root, reg, d, config);
+
+ platform_set_drvdata(pdev, root);
+
+ return 0;
+}
+
+static int qcom_stats_remove(struct platform_device *pdev)
+{
+ struct dentry *root = platform_get_drvdata(pdev);
+
+ debugfs_remove_recursive(root);
+
+ return 0;
+}
+
+static const struct stats_config rpm_data = {
+ .stats_offset = 0,
+ .num_records = 2,
+ .appended_stats_avail = true,
+ .dynamic_offset = true,
+ .subsystem_stats_in_smem = false,
+};
+
+static const struct stats_config rpmh_data = {
+ .stats_offset = 0x48,
+ .num_records = 3,
+ .appended_stats_avail = false,
+ .dynamic_offset = false,
+ .subsystem_stats_in_smem = true,
+};
+
+static const struct of_device_id qcom_stats_table[] = {
+ { .compatible = "qcom,rpm-stats", .data = &rpm_data },
+ { .compatible = "qcom,rpmh-stats", .data = &rpmh_data },
+ { }
+};
+MODULE_DEVICE_TABLE(of, qcom_stats_table);
+
+static struct platform_driver qcom_stats = {
+ .probe = qcom_stats_probe,
+ .remove = qcom_stats_remove,
+ .driver = {
+ .name = "qcom_stats",
+ .of_match_table = qcom_stats_table,
+ },
+};
+
+static int __init qcom_stats_init(void)
+{
+ return platform_driver_register(&qcom_stats);
+}
+late_initcall(qcom_stats_init);
+
+static void __exit qcom_stats_exit(void)
+{
+ platform_driver_unregister(&qcom_stats);
+}
+module_exit(qcom_stats_exit)
+
+MODULE_DESCRIPTION("Qualcomm Technologies, Inc. (QTI) Stats driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c
index e749a2b285d8..3a12a482f6b2 100644
--- a/drivers/soc/qcom/rpmh-rsc.c
+++ b/drivers/soc/qcom/rpmh-rsc.c
@@ -910,7 +910,6 @@ static int rpmh_rsc_probe(struct platform_device *pdev)
{
struct device_node *dn = pdev->dev.of_node;
struct rsc_drv *drv;
- struct resource *res;
char drv_id[10] = {0};
int ret, irq;
u32 solver_config;
@@ -941,8 +940,7 @@ static int rpmh_rsc_probe(struct platform_device *pdev)
drv->name = dev_name(&pdev->dev);
snprintf(drv_id, ARRAY_SIZE(drv_id), "drv-%d", drv->id);
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, drv_id);
- base = devm_ioremap_resource(&pdev->dev, res);
+ base = devm_platform_ioremap_resource_byname(pdev, drv_id);
if (IS_ERR(base))
return PTR_ERR(base);
diff --git a/drivers/soc/qcom/rpmhpd.c b/drivers/soc/qcom/rpmhpd.c
index fa209b479ab3..1118345d8824 100644
--- a/drivers/soc/qcom/rpmhpd.c
+++ b/drivers/soc/qcom/rpmhpd.c
@@ -30,6 +30,7 @@
* @active_only: True if it represents an Active only peer
* @corner: current corner
* @active_corner: current active corner
+ * @enable_corner: lowest non-zero corner
* @level: An array of level (vlvl) to corner (hlvl) mappings
* derived from cmd-db
* @level_count: Number of levels supported by the power domain. max
@@ -47,6 +48,7 @@ struct rpmhpd {
const bool active_only;
unsigned int corner;
unsigned int active_corner;
+ unsigned int enable_corner;
u32 level[RPMH_ARC_MAX_LEVELS];
size_t level_count;
bool enabled;
@@ -147,6 +149,21 @@ static const struct rpmhpd_desc sdx55_desc = {
.num_pds = ARRAY_SIZE(sdx55_rpmhpds),
};
+/* SM6350 RPMH powerdomains */
+static struct rpmhpd *sm6350_rpmhpds[] = {
+ [SM6350_CX] = &sdm845_cx,
+ [SM6350_GFX] = &sdm845_gfx,
+ [SM6350_LCX] = &sdm845_lcx,
+ [SM6350_LMX] = &sdm845_lmx,
+ [SM6350_MSS] = &sdm845_mss,
+ [SM6350_MX] = &sdm845_mx,
+};
+
+static const struct rpmhpd_desc sm6350_desc = {
+ .rpmhpds = sm6350_rpmhpds,
+ .num_pds = ARRAY_SIZE(sm6350_rpmhpds),
+};
+
/* SM8150 RPMH powerdomains */
static struct rpmhpd sm8150_mmcx_ao;
@@ -204,7 +221,7 @@ static const struct rpmhpd_desc sm8250_desc = {
static struct rpmhpd sm8350_mxc_ao;
static struct rpmhpd sm8350_mxc = {
.pd = { .name = "mxc", },
- .peer = &sm8150_mmcx_ao,
+ .peer = &sm8350_mxc_ao,
.res_name = "mxc.lvl",
};
@@ -297,6 +314,7 @@ static const struct of_device_id rpmhpd_match_table[] = {
{ .compatible = "qcom,sc8180x-rpmhpd", .data = &sc8180x_desc },
{ .compatible = "qcom,sdm845-rpmhpd", .data = &sdm845_desc },
{ .compatible = "qcom,sdx55-rpmhpd", .data = &sdx55_desc},
+ { .compatible = "qcom,sm6350-rpmhpd", .data = &sm6350_desc },
{ .compatible = "qcom,sm8150-rpmhpd", .data = &sm8150_desc },
{ .compatible = "qcom,sm8250-rpmhpd", .data = &sm8250_desc },
{ .compatible = "qcom,sm8350-rpmhpd", .data = &sm8350_desc },
@@ -385,13 +403,13 @@ static int rpmhpd_aggregate_corner(struct rpmhpd *pd, unsigned int corner)
static int rpmhpd_power_on(struct generic_pm_domain *domain)
{
struct rpmhpd *pd = domain_to_rpmhpd(domain);
- int ret = 0;
+ unsigned int corner;
+ int ret;
mutex_lock(&rpmhpd_lock);
- if (pd->corner)
- ret = rpmhpd_aggregate_corner(pd, pd->corner);
-
+ corner = max(pd->corner, pd->enable_corner);
+ ret = rpmhpd_aggregate_corner(pd, corner);
if (!ret)
pd->enabled = true;
@@ -436,6 +454,10 @@ static int rpmhpd_set_performance_state(struct generic_pm_domain *domain,
i--;
if (pd->enabled) {
+ /* Ensure that the domain isn't turn off */
+ if (i < pd->enable_corner)
+ i = pd->enable_corner;
+
ret = rpmhpd_aggregate_corner(pd, i);
if (ret)
goto out;
@@ -472,6 +494,10 @@ static int rpmhpd_update_level_mapping(struct rpmhpd *rpmhpd)
for (i = 0; i < rpmhpd->level_count; i++) {
rpmhpd->level[i] = buf[i];
+ /* Remember the first corner with non-zero level */
+ if (!rpmhpd->level[rpmhpd->enable_corner] && rpmhpd->level[i])
+ rpmhpd->enable_corner = i;
+
/*
* The AUX data may be zero padded. These 0 valued entries at
* the end of the map must be ignored.
diff --git a/drivers/soc/qcom/rpmpd.c b/drivers/soc/qcom/rpmpd.c
index dbf494e92574..4f69fb9b2e0e 100644
--- a/drivers/soc/qcom/rpmpd.c
+++ b/drivers/soc/qcom/rpmpd.c
@@ -185,6 +185,29 @@ static const struct rpmpd_desc msm8916_desc = {
.max_state = MAX_CORNER_RPMPD_STATE,
};
+/* msm8953 RPM Power Domains */
+DEFINE_RPMPD_PAIR(msm8953, vddmd, vddmd_ao, SMPA, LEVEL, 1);
+DEFINE_RPMPD_PAIR(msm8953, vddcx, vddcx_ao, SMPA, LEVEL, 2);
+DEFINE_RPMPD_PAIR(msm8953, vddmx, vddmx_ao, SMPA, LEVEL, 7);
+
+DEFINE_RPMPD_VFL(msm8953, vddcx_vfl, SMPA, 2);
+
+static struct rpmpd *msm8953_rpmpds[] = {
+ [MSM8953_VDDMD] = &msm8953_vddmd,
+ [MSM8953_VDDMD_AO] = &msm8953_vddmd_ao,
+ [MSM8953_VDDCX] = &msm8953_vddcx,
+ [MSM8953_VDDCX_AO] = &msm8953_vddcx_ao,
+ [MSM8953_VDDCX_VFL] = &msm8953_vddcx_vfl,
+ [MSM8953_VDDMX] = &msm8953_vddmx,
+ [MSM8953_VDDMX_AO] = &msm8953_vddmx_ao,
+};
+
+static const struct rpmpd_desc msm8953_desc = {
+ .rpmpds = msm8953_rpmpds,
+ .num_pds = ARRAY_SIZE(msm8953_rpmpds),
+ .max_state = RPM_SMD_LEVEL_TURBO,
+};
+
/* msm8976 RPM Power Domains */
DEFINE_RPMPD_PAIR(msm8976, vddcx, vddcx_ao, SMPA, LEVEL, 2);
DEFINE_RPMPD_PAIR(msm8976, vddmx, vddmx_ao, SMPA, LEVEL, 6);
@@ -377,6 +400,7 @@ static const struct of_device_id rpmpd_match_table[] = {
{ .compatible = "qcom,mdm9607-rpmpd", .data = &mdm9607_desc },
{ .compatible = "qcom,msm8916-rpmpd", .data = &msm8916_desc },
{ .compatible = "qcom,msm8939-rpmpd", .data = &msm8939_desc },
+ { .compatible = "qcom,msm8953-rpmpd", .data = &msm8953_desc },
{ .compatible = "qcom,msm8976-rpmpd", .data = &msm8976_desc },
{ .compatible = "qcom,msm8994-rpmpd", .data = &msm8994_desc },
{ .compatible = "qcom,msm8996-rpmpd", .data = &msm8996_desc },
diff --git a/drivers/soc/qcom/smd-rpm.c b/drivers/soc/qcom/smd-rpm.c
index dfdd4f20f5fd..30dda1af63c8 100644
--- a/drivers/soc/qcom/smd-rpm.c
+++ b/drivers/soc/qcom/smd-rpm.c
@@ -236,6 +236,7 @@ static const struct of_device_id qcom_smd_rpm_of_match[] = {
{ .compatible = "qcom,rpm-msm8226" },
{ .compatible = "qcom,rpm-msm8916" },
{ .compatible = "qcom,rpm-msm8936" },
+ { .compatible = "qcom,rpm-msm8953" },
{ .compatible = "qcom,rpm-msm8974" },
{ .compatible = "qcom,rpm-msm8976" },
{ .compatible = "qcom,rpm-msm8994" },
@@ -244,6 +245,7 @@ static const struct of_device_id qcom_smd_rpm_of_match[] = {
{ .compatible = "qcom,rpm-sdm660" },
{ .compatible = "qcom,rpm-sm6115" },
{ .compatible = "qcom,rpm-sm6125" },
+ { .compatible = "qcom,rpm-qcm2290" },
{ .compatible = "qcom,rpm-qcs404" },
{}
};
diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c
index 4fb5aeeb0843..c7e519bfdc8a 100644
--- a/drivers/soc/qcom/smem.c
+++ b/drivers/soc/qcom/smem.c
@@ -9,6 +9,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
+#include <linux/of_reserved_mem.h>
#include <linux/platform_device.h>
#include <linux/sizes.h>
#include <linux/slab.h>
@@ -240,7 +241,7 @@ static const u8 SMEM_INFO_MAGIC[] = { 0x53, 0x49, 0x49, 0x49 }; /* SIII */
* @size: size of the memory region
*/
struct smem_region {
- u32 aux_base;
+ phys_addr_t aux_base;
void __iomem *virt_base;
size_t size;
};
@@ -499,7 +500,7 @@ static void *qcom_smem_get_global(struct qcom_smem *smem,
for (i = 0; i < smem->num_regions; i++) {
region = &smem->regions[i];
- if (region->aux_base == aux_base || !aux_base) {
+ if ((u32)region->aux_base == aux_base || !aux_base) {
if (size != NULL)
*size = le32_to_cpu(entry->size);
return region->virt_base + le32_to_cpu(entry->offset);
@@ -664,7 +665,7 @@ phys_addr_t qcom_smem_virt_to_phys(void *p)
if (p < region->virt_base + region->size) {
u64 offset = p - region->virt_base;
- return (phys_addr_t)region->aux_base + offset;
+ return region->aux_base + offset;
}
}
@@ -863,12 +864,12 @@ qcom_smem_enumerate_partitions(struct qcom_smem *smem, u16 local_host)
return 0;
}
-static int qcom_smem_map_memory(struct qcom_smem *smem, struct device *dev,
- const char *name, int i)
+static int qcom_smem_resolve_mem(struct qcom_smem *smem, const char *name,
+ struct smem_region *region)
{
+ struct device *dev = smem->dev;
struct device_node *np;
struct resource r;
- resource_size_t size;
int ret;
np = of_parse_phandle(dev->of_node, name, 0);
@@ -881,13 +882,9 @@ static int qcom_smem_map_memory(struct qcom_smem *smem, struct device *dev,
of_node_put(np);
if (ret)
return ret;
- size = resource_size(&r);
- smem->regions[i].virt_base = devm_ioremap_wc(dev, r.start, size);
- if (!smem->regions[i].virt_base)
- return -ENOMEM;
- smem->regions[i].aux_base = (u32)r.start;
- smem->regions[i].size = size;
+ region->aux_base = r.start;
+ region->size = resource_size(&r);
return 0;
}
@@ -895,12 +892,14 @@ static int qcom_smem_map_memory(struct qcom_smem *smem, struct device *dev,
static int qcom_smem_probe(struct platform_device *pdev)
{
struct smem_header *header;
+ struct reserved_mem *rmem;
struct qcom_smem *smem;
size_t array_size;
int num_regions;
int hwlock_id;
u32 version;
int ret;
+ int i;
num_regions = 1;
if (of_find_property(pdev->dev.of_node, "qcom,rpm-msg-ram", NULL))
@@ -914,13 +913,35 @@ static int qcom_smem_probe(struct platform_device *pdev)
smem->dev = &pdev->dev;
smem->num_regions = num_regions;
- ret = qcom_smem_map_memory(smem, &pdev->dev, "memory-region", 0);
- if (ret)
- return ret;
+ rmem = of_reserved_mem_lookup(pdev->dev.of_node);
+ if (rmem) {
+ smem->regions[0].aux_base = rmem->base;
+ smem->regions[0].size = rmem->size;
+ } else {
+ /*
+ * Fall back to the memory-region reference, if we're not a
+ * reserved-memory node.
+ */
+ ret = qcom_smem_resolve_mem(smem, "memory-region", &smem->regions[0]);
+ if (ret)
+ return ret;
+ }
- if (num_regions > 1 && (ret = qcom_smem_map_memory(smem, &pdev->dev,
- "qcom,rpm-msg-ram", 1)))
- return ret;
+ if (num_regions > 1) {
+ ret = qcom_smem_resolve_mem(smem, "qcom,rpm-msg-ram", &smem->regions[1]);
+ if (ret)
+ return ret;
+ }
+
+ for (i = 0; i < num_regions; i++) {
+ smem->regions[i].virt_base = devm_ioremap_wc(&pdev->dev,
+ smem->regions[i].aux_base,
+ smem->regions[i].size);
+ if (!smem->regions[i].virt_base) {
+ dev_err(&pdev->dev, "failed to remap %pa\n", &smem->regions[i].aux_base);
+ return -ENOMEM;
+ }
+ }
header = smem->regions[0].virt_base;
if (le32_to_cpu(header->initialized) != 1 ||
diff --git a/drivers/soc/qcom/smp2p.c b/drivers/soc/qcom/smp2p.c
index 2df488333be9..4a157240f419 100644
--- a/drivers/soc/qcom/smp2p.c
+++ b/drivers/soc/qcom/smp2p.c
@@ -14,6 +14,7 @@
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/pm_wakeirq.h>
#include <linux/regmap.h>
#include <linux/soc/qcom/smem.h>
#include <linux/soc/qcom/smem_state.h>
@@ -40,8 +41,11 @@
#define SMP2P_MAX_ENTRY_NAME 16
#define SMP2P_FEATURE_SSR_ACK 0x1
+#define SMP2P_FLAGS_RESTART_DONE_BIT 0
+#define SMP2P_FLAGS_RESTART_ACK_BIT 1
#define SMP2P_MAGIC 0x504d5324
+#define SMP2P_ALL_FEATURES SMP2P_FEATURE_SSR_ACK
/**
* struct smp2p_smem_item - in memory communication structure
@@ -135,6 +139,10 @@ struct qcom_smp2p {
unsigned valid_entries;
+ bool ssr_ack_enabled;
+ bool ssr_ack;
+ bool negotiation_done;
+
unsigned local_pid;
unsigned remote_pid;
@@ -162,22 +170,53 @@ static void qcom_smp2p_kick(struct qcom_smp2p *smp2p)
}
}
-/**
- * qcom_smp2p_intr() - interrupt handler for incoming notifications
- * @irq: unused
- * @data: smp2p driver context
- *
- * Handle notifications from the remote side to handle newly allocated entries
- * or any changes to the state bits of existing entries.
- */
-static irqreturn_t qcom_smp2p_intr(int irq, void *data)
+static bool qcom_smp2p_check_ssr(struct qcom_smp2p *smp2p)
+{
+ struct smp2p_smem_item *in = smp2p->in;
+ bool restart;
+
+ if (!smp2p->ssr_ack_enabled)
+ return false;
+
+ restart = in->flags & BIT(SMP2P_FLAGS_RESTART_DONE_BIT);
+
+ return restart != smp2p->ssr_ack;
+}
+
+static void qcom_smp2p_do_ssr_ack(struct qcom_smp2p *smp2p)
+{
+ struct smp2p_smem_item *out = smp2p->out;
+ u32 val;
+
+ smp2p->ssr_ack = !smp2p->ssr_ack;
+
+ val = out->flags & ~BIT(SMP2P_FLAGS_RESTART_ACK_BIT);
+ if (smp2p->ssr_ack)
+ val |= BIT(SMP2P_FLAGS_RESTART_ACK_BIT);
+ out->flags = val;
+
+ qcom_smp2p_kick(smp2p);
+}
+
+static void qcom_smp2p_negotiate(struct qcom_smp2p *smp2p)
+{
+ struct smp2p_smem_item *out = smp2p->out;
+ struct smp2p_smem_item *in = smp2p->in;
+
+ if (in->version == out->version) {
+ out->features &= in->features;
+
+ if (out->features & SMP2P_FEATURE_SSR_ACK)
+ smp2p->ssr_ack_enabled = true;
+
+ smp2p->negotiation_done = true;
+ }
+}
+
+static void qcom_smp2p_notify_in(struct qcom_smp2p *smp2p)
{
struct smp2p_smem_item *in;
struct smp2p_entry *entry;
- struct qcom_smp2p *smp2p = data;
- unsigned smem_id = smp2p->smem_items[SMP2P_INBOUND];
- unsigned pid = smp2p->remote_pid;
- size_t size;
int irq_pin;
u32 status;
char buf[SMP2P_MAX_ENTRY_NAME];
@@ -186,18 +225,6 @@ static irqreturn_t qcom_smp2p_intr(int irq, void *data)
in = smp2p->in;
- /* Acquire smem item, if not already found */
- if (!in) {
- in = qcom_smem_get(pid, smem_id, &size);
- if (IS_ERR(in)) {
- dev_err(smp2p->dev,
- "Unable to acquire remote smp2p item\n");
- return IRQ_HANDLED;
- }
-
- smp2p->in = in;
- }
-
/* Match newly created entries */
for (i = smp2p->valid_entries; i < in->valid_entries; i++) {
list_for_each_entry(entry, &smp2p->inbound, node) {
@@ -236,7 +263,51 @@ static irqreturn_t qcom_smp2p_intr(int irq, void *data)
}
}
}
+}
+
+/**
+ * qcom_smp2p_intr() - interrupt handler for incoming notifications
+ * @irq: unused
+ * @data: smp2p driver context
+ *
+ * Handle notifications from the remote side to handle newly allocated entries
+ * or any changes to the state bits of existing entries.
+ */
+static irqreturn_t qcom_smp2p_intr(int irq, void *data)
+{
+ struct smp2p_smem_item *in;
+ struct qcom_smp2p *smp2p = data;
+ unsigned int smem_id = smp2p->smem_items[SMP2P_INBOUND];
+ unsigned int pid = smp2p->remote_pid;
+ bool ack_restart;
+ size_t size;
+
+ in = smp2p->in;
+
+ /* Acquire smem item, if not already found */
+ if (!in) {
+ in = qcom_smem_get(pid, smem_id, &size);
+ if (IS_ERR(in)) {
+ dev_err(smp2p->dev,
+ "Unable to acquire remote smp2p item\n");
+ goto out;
+ }
+
+ smp2p->in = in;
+ }
+
+ if (!smp2p->negotiation_done)
+ qcom_smp2p_negotiate(smp2p);
+
+ if (smp2p->negotiation_done) {
+ ack_restart = qcom_smp2p_check_ssr(smp2p);
+ qcom_smp2p_notify_in(smp2p);
+
+ if (ack_restart)
+ qcom_smp2p_do_ssr_ack(smp2p);
+ }
+out:
return IRQ_HANDLED;
}
@@ -392,6 +463,7 @@ static int qcom_smp2p_alloc_outbound_item(struct qcom_smp2p *smp2p)
out->remote_pid = smp2p->remote_pid;
out->total_entries = SMP2P_MAX_ENTRY;
out->valid_entries = 0;
+ out->features = SMP2P_ALL_FEATURES;
/*
* Make sure the rest of the header is written before we validate the
@@ -501,6 +573,7 @@ static int qcom_smp2p_probe(struct platform_device *pdev)
entry = devm_kzalloc(&pdev->dev, sizeof(*entry), GFP_KERNEL);
if (!entry) {
ret = -ENOMEM;
+ of_node_put(node);
goto unwind_interfaces;
}
@@ -508,19 +581,25 @@ static int qcom_smp2p_probe(struct platform_device *pdev)
spin_lock_init(&entry->lock);
ret = of_property_read_string(node, "qcom,entry-name", &entry->name);
- if (ret < 0)
+ if (ret < 0) {
+ of_node_put(node);
goto unwind_interfaces;
+ }
if (of_property_read_bool(node, "interrupt-controller")) {
ret = qcom_smp2p_inbound_entry(smp2p, entry, node);
- if (ret < 0)
+ if (ret < 0) {
+ of_node_put(node);
goto unwind_interfaces;
+ }
list_add(&entry->node, &smp2p->inbound);
} else {
ret = qcom_smp2p_outbound_entry(smp2p, entry, node);
- if (ret < 0)
+ if (ret < 0) {
+ of_node_put(node);
goto unwind_interfaces;
+ }
list_add(&entry->node, &smp2p->outbound);
}
@@ -538,9 +617,26 @@ static int qcom_smp2p_probe(struct platform_device *pdev)
goto unwind_interfaces;
}
+ /*
+ * Treat smp2p interrupt as wakeup source, but keep it disabled
+ * by default. User space can decide enabling it depending on its
+ * use cases. For example if remoteproc crashes and device wants
+ * to handle it immediatedly (e.g. to not miss phone calls) it can
+ * enable wakeup source from user space, while other devices which
+ * do not have proper autosleep feature may want to handle it with
+ * other wakeup events (e.g. Power button) instead waking up immediately.
+ */
+ device_set_wakeup_capable(&pdev->dev, true);
+
+ ret = dev_pm_set_wake_irq(&pdev->dev, irq);
+ if (ret)
+ goto set_wake_irq_fail;
return 0;
+set_wake_irq_fail:
+ dev_pm_clear_wake_irq(&pdev->dev);
+
unwind_interfaces:
list_for_each_entry(entry, &smp2p->inbound, node)
irq_domain_remove(entry->domain);
@@ -565,6 +661,8 @@ static int qcom_smp2p_remove(struct platform_device *pdev)
struct qcom_smp2p *smp2p = platform_get_drvdata(pdev);
struct smp2p_entry *entry;
+ dev_pm_clear_wake_irq(&pdev->dev);
+
list_for_each_entry(entry, &smp2p->inbound, node)
irq_domain_remove(entry->domain);
diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c
index 52e581167115..9a0eb59405e8 100644
--- a/drivers/soc/qcom/socinfo.c
+++ b/drivers/soc/qcom/socinfo.c
@@ -87,8 +87,8 @@ static const char *const pmic_models[] = {
[15] = "PM8901",
[16] = "PM8950/PM8027",
[17] = "PMI8950/ISL9519",
- [18] = "PM8921",
- [19] = "PM8018",
+ [18] = "PMK8001/PM8921",
+ [19] = "PMI8996/PM8018",
[20] = "PM8998/PM8015",
[21] = "PMI8998/PM8014",
[22] = "PM8821",
@@ -102,6 +102,8 @@ static const char *const pmic_models[] = {
[32] = "PM8150B",
[33] = "PMK8002",
[36] = "PM8009",
+ [38] = "PM8150C",
+ [41] = "SMB2351",
};
#endif /* CONFIG_DEBUG_FS */
@@ -281,19 +283,31 @@ static const struct soc_id soc_id[] = {
{ 319, "APQ8098" },
{ 321, "SDM845" },
{ 322, "MDM9206" },
+ { 323, "IPQ8074" },
{ 324, "SDA660" },
{ 325, "SDM658" },
{ 326, "SDA658" },
{ 327, "SDA630" },
{ 338, "SDM450" },
{ 341, "SDA845" },
+ { 342, "IPQ8072" },
+ { 343, "IPQ8076" },
+ { 344, "IPQ8078" },
{ 345, "SDM636" },
{ 346, "SDA636" },
{ 349, "SDM632" },
{ 350, "SDA632" },
{ 351, "SDA450" },
{ 356, "SM8250" },
+ { 375, "IPQ8070" },
+ { 376, "IPQ8071" },
+ { 389, "IPQ8072A" },
+ { 390, "IPQ8074A" },
+ { 391, "IPQ8076A" },
+ { 392, "IPQ8078A" },
{ 394, "SM6125" },
+ { 395, "IPQ8070A" },
+ { 396, "IPQ8071A" },
{ 402, "IPQ6018" },
{ 403, "IPQ6028" },
{ 421, "IPQ6000" },
diff --git a/drivers/soc/qcom/spm.c b/drivers/soc/qcom/spm.c
new file mode 100644
index 000000000000..f831420b7fd4
--- /dev/null
+++ b/drivers/soc/qcom/spm.c
@@ -0,0 +1,279 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2011-2014, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014,2015, Linaro Ltd.
+ *
+ * SAW power controller driver
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <soc/qcom/spm.h>
+
+#define SPM_CTL_INDEX 0x7f
+#define SPM_CTL_INDEX_SHIFT 4
+#define SPM_CTL_EN BIT(0)
+
+enum spm_reg {
+ SPM_REG_CFG,
+ SPM_REG_SPM_CTL,
+ SPM_REG_DLY,
+ SPM_REG_PMIC_DLY,
+ SPM_REG_PMIC_DATA_0,
+ SPM_REG_PMIC_DATA_1,
+ SPM_REG_VCTL,
+ SPM_REG_SEQ_ENTRY,
+ SPM_REG_SPM_STS,
+ SPM_REG_PMIC_STS,
+ SPM_REG_AVS_CTL,
+ SPM_REG_AVS_LIMIT,
+ SPM_REG_NR,
+};
+
+static const u16 spm_reg_offset_v4_1[SPM_REG_NR] = {
+ [SPM_REG_AVS_CTL] = 0x904,
+ [SPM_REG_AVS_LIMIT] = 0x908,
+};
+
+static const struct spm_reg_data spm_reg_660_gold_l2 = {
+ .reg_offset = spm_reg_offset_v4_1,
+ .avs_ctl = 0x1010031,
+ .avs_limit = 0x4580458,
+};
+
+static const struct spm_reg_data spm_reg_660_silver_l2 = {
+ .reg_offset = spm_reg_offset_v4_1,
+ .avs_ctl = 0x101c031,
+ .avs_limit = 0x4580458,
+};
+
+static const struct spm_reg_data spm_reg_8998_gold_l2 = {
+ .reg_offset = spm_reg_offset_v4_1,
+ .avs_ctl = 0x1010031,
+ .avs_limit = 0x4700470,
+};
+
+static const struct spm_reg_data spm_reg_8998_silver_l2 = {
+ .reg_offset = spm_reg_offset_v4_1,
+ .avs_ctl = 0x1010031,
+ .avs_limit = 0x4200420,
+};
+
+static const u16 spm_reg_offset_v3_0[SPM_REG_NR] = {
+ [SPM_REG_CFG] = 0x08,
+ [SPM_REG_SPM_CTL] = 0x30,
+ [SPM_REG_DLY] = 0x34,
+ [SPM_REG_SEQ_ENTRY] = 0x400,
+};
+
+/* SPM register data for 8916 */
+static const struct spm_reg_data spm_reg_8916_cpu = {
+ .reg_offset = spm_reg_offset_v3_0,
+ .spm_cfg = 0x1,
+ .spm_dly = 0x3C102800,
+ .seq = { 0x60, 0x03, 0x60, 0x0B, 0x0F, 0x20, 0x10, 0x80, 0x30, 0x90,
+ 0x5B, 0x60, 0x03, 0x60, 0x3B, 0x76, 0x76, 0x0B, 0x94, 0x5B,
+ 0x80, 0x10, 0x26, 0x30, 0x0F },
+ .start_index[PM_SLEEP_MODE_STBY] = 0,
+ .start_index[PM_SLEEP_MODE_SPC] = 5,
+};
+
+static const u16 spm_reg_offset_v2_1[SPM_REG_NR] = {
+ [SPM_REG_CFG] = 0x08,
+ [SPM_REG_SPM_CTL] = 0x30,
+ [SPM_REG_DLY] = 0x34,
+ [SPM_REG_SEQ_ENTRY] = 0x80,
+};
+
+/* SPM register data for 8974, 8084 */
+static const struct spm_reg_data spm_reg_8974_8084_cpu = {
+ .reg_offset = spm_reg_offset_v2_1,
+ .spm_cfg = 0x1,
+ .spm_dly = 0x3C102800,
+ .seq = { 0x03, 0x0B, 0x0F, 0x00, 0x20, 0x80, 0x10, 0xE8, 0x5B, 0x03,
+ 0x3B, 0xE8, 0x5B, 0x82, 0x10, 0x0B, 0x30, 0x06, 0x26, 0x30,
+ 0x0F },
+ .start_index[PM_SLEEP_MODE_STBY] = 0,
+ .start_index[PM_SLEEP_MODE_SPC] = 3,
+};
+
+/* SPM register data for 8226 */
+static const struct spm_reg_data spm_reg_8226_cpu = {
+ .reg_offset = spm_reg_offset_v2_1,
+ .spm_cfg = 0x0,
+ .spm_dly = 0x3C102800,
+ .seq = { 0x60, 0x03, 0x60, 0x0B, 0x0F, 0x20, 0x10, 0x80, 0x30, 0x90,
+ 0x5B, 0x60, 0x03, 0x60, 0x3B, 0x76, 0x76, 0x0B, 0x94, 0x5B,
+ 0x80, 0x10, 0x26, 0x30, 0x0F },
+ .start_index[PM_SLEEP_MODE_STBY] = 0,
+ .start_index[PM_SLEEP_MODE_SPC] = 5,
+};
+
+static const u16 spm_reg_offset_v1_1[SPM_REG_NR] = {
+ [SPM_REG_CFG] = 0x08,
+ [SPM_REG_SPM_CTL] = 0x20,
+ [SPM_REG_PMIC_DLY] = 0x24,
+ [SPM_REG_PMIC_DATA_0] = 0x28,
+ [SPM_REG_PMIC_DATA_1] = 0x2C,
+ [SPM_REG_SEQ_ENTRY] = 0x80,
+};
+
+/* SPM register data for 8064 */
+static const struct spm_reg_data spm_reg_8064_cpu = {
+ .reg_offset = spm_reg_offset_v1_1,
+ .spm_cfg = 0x1F,
+ .pmic_dly = 0x02020004,
+ .pmic_data[0] = 0x0084009C,
+ .pmic_data[1] = 0x00A4001C,
+ .seq = { 0x03, 0x0F, 0x00, 0x24, 0x54, 0x10, 0x09, 0x03, 0x01,
+ 0x10, 0x54, 0x30, 0x0C, 0x24, 0x30, 0x0F },
+ .start_index[PM_SLEEP_MODE_STBY] = 0,
+ .start_index[PM_SLEEP_MODE_SPC] = 2,
+};
+
+static inline void spm_register_write(struct spm_driver_data *drv,
+ enum spm_reg reg, u32 val)
+{
+ if (drv->reg_data->reg_offset[reg])
+ writel_relaxed(val, drv->reg_base +
+ drv->reg_data->reg_offset[reg]);
+}
+
+/* Ensure a guaranteed write, before return */
+static inline void spm_register_write_sync(struct spm_driver_data *drv,
+ enum spm_reg reg, u32 val)
+{
+ u32 ret;
+
+ if (!drv->reg_data->reg_offset[reg])
+ return;
+
+ do {
+ writel_relaxed(val, drv->reg_base +
+ drv->reg_data->reg_offset[reg]);
+ ret = readl_relaxed(drv->reg_base +
+ drv->reg_data->reg_offset[reg]);
+ if (ret == val)
+ break;
+ cpu_relax();
+ } while (1);
+}
+
+static inline u32 spm_register_read(struct spm_driver_data *drv,
+ enum spm_reg reg)
+{
+ return readl_relaxed(drv->reg_base + drv->reg_data->reg_offset[reg]);
+}
+
+void spm_set_low_power_mode(struct spm_driver_data *drv,
+ enum pm_sleep_mode mode)
+{
+ u32 start_index;
+ u32 ctl_val;
+
+ start_index = drv->reg_data->start_index[mode];
+
+ ctl_val = spm_register_read(drv, SPM_REG_SPM_CTL);
+ ctl_val &= ~(SPM_CTL_INDEX << SPM_CTL_INDEX_SHIFT);
+ ctl_val |= start_index << SPM_CTL_INDEX_SHIFT;
+ ctl_val |= SPM_CTL_EN;
+ spm_register_write_sync(drv, SPM_REG_SPM_CTL, ctl_val);
+}
+
+static const struct of_device_id spm_match_table[] = {
+ { .compatible = "qcom,sdm660-gold-saw2-v4.1-l2",
+ .data = &spm_reg_660_gold_l2 },
+ { .compatible = "qcom,sdm660-silver-saw2-v4.1-l2",
+ .data = &spm_reg_660_silver_l2 },
+ { .compatible = "qcom,msm8226-saw2-v2.1-cpu",
+ .data = &spm_reg_8226_cpu },
+ { .compatible = "qcom,msm8916-saw2-v3.0-cpu",
+ .data = &spm_reg_8916_cpu },
+ { .compatible = "qcom,msm8974-saw2-v2.1-cpu",
+ .data = &spm_reg_8974_8084_cpu },
+ { .compatible = "qcom,msm8998-gold-saw2-v4.1-l2",
+ .data = &spm_reg_8998_gold_l2 },
+ { .compatible = "qcom,msm8998-silver-saw2-v4.1-l2",
+ .data = &spm_reg_8998_silver_l2 },
+ { .compatible = "qcom,apq8084-saw2-v2.1-cpu",
+ .data = &spm_reg_8974_8084_cpu },
+ { .compatible = "qcom,apq8064-saw2-v1.1-cpu",
+ .data = &spm_reg_8064_cpu },
+ { },
+};
+MODULE_DEVICE_TABLE(of, spm_match_table);
+
+static int spm_dev_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *match_id;
+ struct spm_driver_data *drv;
+ struct resource *res;
+ void __iomem *addr;
+
+ drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
+ if (!drv)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ drv->reg_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(drv->reg_base))
+ return PTR_ERR(drv->reg_base);
+
+ match_id = of_match_node(spm_match_table, pdev->dev.of_node);
+ if (!match_id)
+ return -ENODEV;
+
+ drv->reg_data = match_id->data;
+ platform_set_drvdata(pdev, drv);
+
+ /* Write the SPM sequences first.. */
+ addr = drv->reg_base + drv->reg_data->reg_offset[SPM_REG_SEQ_ENTRY];
+ __iowrite32_copy(addr, drv->reg_data->seq,
+ ARRAY_SIZE(drv->reg_data->seq) / 4);
+
+ /*
+ * ..and then the control registers.
+ * On some SoC if the control registers are written first and if the
+ * CPU was held in reset, the reset signal could trigger the SPM state
+ * machine, before the sequences are completely written.
+ */
+ spm_register_write(drv, SPM_REG_AVS_CTL, drv->reg_data->avs_ctl);
+ spm_register_write(drv, SPM_REG_AVS_LIMIT, drv->reg_data->avs_limit);
+ spm_register_write(drv, SPM_REG_CFG, drv->reg_data->spm_cfg);
+ spm_register_write(drv, SPM_REG_DLY, drv->reg_data->spm_dly);
+ spm_register_write(drv, SPM_REG_PMIC_DLY, drv->reg_data->pmic_dly);
+ spm_register_write(drv, SPM_REG_PMIC_DATA_0,
+ drv->reg_data->pmic_data[0]);
+ spm_register_write(drv, SPM_REG_PMIC_DATA_1,
+ drv->reg_data->pmic_data[1]);
+
+ /* Set up Standby as the default low power mode */
+ if (drv->reg_data->reg_offset[SPM_REG_SPM_CTL])
+ spm_set_low_power_mode(drv, PM_SLEEP_MODE_STBY);
+
+ return 0;
+}
+
+static struct platform_driver spm_driver = {
+ .probe = spm_dev_probe,
+ .driver = {
+ .name = "qcom_spm",
+ .of_match_table = spm_match_table,
+ },
+};
+
+static int __init qcom_spm_init(void)
+{
+ return platform_driver_register(&spm_driver);
+}
+arch_initcall(qcom_spm_init);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig
index 07e0ecd64319..ce16ef5c939c 100644
--- a/drivers/soc/renesas/Kconfig
+++ b/drivers/soc/renesas/Kconfig
@@ -186,6 +186,7 @@ config ARCH_R8A77995
select SYSC_R8A77995
help
This enables support for the Renesas R-Car D3 SoC.
+ This includes different gradings like R-Car D3e.
config ARCH_R8A77990
bool "ARM64 Platform support for R-Car E3"
@@ -193,6 +194,7 @@ config ARCH_R8A77990
select SYSC_R8A77990
help
This enables support for the Renesas R-Car E3 SoC.
+ This includes different gradings like R-Car E3e.
config ARCH_R8A77950
bool "ARM64 Platform support for R-Car H3 ES1.x"
@@ -208,7 +210,7 @@ config ARCH_R8A77951
help
This enables support for the Renesas R-Car H3 SoC (revisions 2.0 and
later).
- This includes different gradings like R-Car H3e-2G.
+ This includes different gradings like R-Car H3e, H3e-2G, and H3Ne.
config ARCH_R8A77965
bool "ARM64 Platform support for R-Car M3-N"
@@ -216,6 +218,7 @@ config ARCH_R8A77965
select SYSC_R8A77965
help
This enables support for the Renesas R-Car M3-N SoC.
+ This includes different gradings like R-Car M3Ne and M3Ne-2G.
config ARCH_R8A77960
bool "ARM64 Platform support for R-Car M3-W"
@@ -230,7 +233,7 @@ config ARCH_R8A77961
select SYSC_R8A77961
help
This enables support for the Renesas R-Car M3-W+ SoC.
- This includes different gradings like R-Car M3e-2G.
+ This includes different gradings like R-Car M3e and M3e-2G.
config ARCH_R8A77980
bool "ARM64 Platform support for R-Car V3H"
diff --git a/drivers/soc/renesas/renesas-soc.c b/drivers/soc/renesas/renesas-soc.c
index dab9f5a0aad0..7961b0be1850 100644
--- a/drivers/soc/renesas/renesas-soc.c
+++ b/drivers/soc/renesas/renesas-soc.c
@@ -285,17 +285,22 @@ static const struct of_device_id renesas_socs[] __initconst = {
{ .compatible = "renesas,r8a7795", .data = &soc_rcar_h3 },
#endif
#ifdef CONFIG_ARCH_R8A77951
+ { .compatible = "renesas,r8a779m0", .data = &soc_rcar_h3 },
{ .compatible = "renesas,r8a779m1", .data = &soc_rcar_h3 },
+ { .compatible = "renesas,r8a779m8", .data = &soc_rcar_h3 },
#endif
#ifdef CONFIG_ARCH_R8A77960
{ .compatible = "renesas,r8a7796", .data = &soc_rcar_m3_w },
#endif
#ifdef CONFIG_ARCH_R8A77961
{ .compatible = "renesas,r8a77961", .data = &soc_rcar_m3_w },
+ { .compatible = "renesas,r8a779m2", .data = &soc_rcar_m3_w },
{ .compatible = "renesas,r8a779m3", .data = &soc_rcar_m3_w },
#endif
#ifdef CONFIG_ARCH_R8A77965
{ .compatible = "renesas,r8a77965", .data = &soc_rcar_m3_n },
+ { .compatible = "renesas,r8a779m4", .data = &soc_rcar_m3_n },
+ { .compatible = "renesas,r8a779m5", .data = &soc_rcar_m3_n },
#endif
#ifdef CONFIG_ARCH_R8A77970
{ .compatible = "renesas,r8a77970", .data = &soc_rcar_v3m },
@@ -305,9 +310,11 @@ static const struct of_device_id renesas_socs[] __initconst = {
#endif
#ifdef CONFIG_ARCH_R8A77990
{ .compatible = "renesas,r8a77990", .data = &soc_rcar_e3 },
+ { .compatible = "renesas,r8a779m6", .data = &soc_rcar_e3 },
#endif
#ifdef CONFIG_ARCH_R8A77995
{ .compatible = "renesas,r8a77995", .data = &soc_rcar_d3 },
+ { .compatible = "renesas,r8a779m7", .data = &soc_rcar_d3 },
#endif
#ifdef CONFIG_ARCH_R8A779A0
{ .compatible = "renesas,r8a779a0", .data = &soc_rcar_v3u },
diff --git a/drivers/soc/samsung/Kconfig b/drivers/soc/samsung/Kconfig
index 5745d7e5908e..e2cedef1e8d1 100644
--- a/drivers/soc/samsung/Kconfig
+++ b/drivers/soc/samsung/Kconfig
@@ -13,18 +13,21 @@ config EXYNOS_ASV_ARM
depends on EXYNOS_CHIPID
config EXYNOS_CHIPID
- bool "Exynos ChipID controller and ASV driver" if COMPILE_TEST
+ tristate "Exynos ChipID controller and ASV driver"
depends on ARCH_EXYNOS || COMPILE_TEST
+ default ARCH_EXYNOS
select EXYNOS_ASV_ARM if ARM && ARCH_EXYNOS
select MFD_SYSCON
select SOC_BUS
help
Support for Samsung Exynos SoC ChipID and Adaptive Supply Voltage.
+ This driver can also be built as module (exynos_chipid).
config EXYNOS_PMU
bool "Exynos PMU controller driver" if COMPILE_TEST
depends on ARCH_EXYNOS || ((ARM || ARM64) && COMPILE_TEST)
select EXYNOS_PMU_ARM_DRIVERS if ARM && ARCH_EXYNOS
+ select MFD_CORE
# There is no need to enable these drivers for ARMv8
config EXYNOS_PMU_ARM_DRIVERS
diff --git a/drivers/soc/samsung/Makefile b/drivers/soc/samsung/Makefile
index 0c523a8de4eb..2ae4bea804cf 100644
--- a/drivers/soc/samsung/Makefile
+++ b/drivers/soc/samsung/Makefile
@@ -1,8 +1,9 @@
# SPDX-License-Identifier: GPL-2.0
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_CHIPID) += exynos-chipid.o exynos-asv.o
obj-$(CONFIG_EXYNOS_PMU) += exynos-pmu.o
obj-$(CONFIG_EXYNOS_PMU_ARM_DRIVERS) += exynos3250-pmu.o exynos4-pmu.o \
diff --git a/drivers/soc/samsung/exynos-chipid.c b/drivers/soc/samsung/exynos-chipid.c
index 5c1d0f97f766..a28053ec7e6a 100644
--- a/drivers/soc/samsung/exynos-chipid.c
+++ b/drivers/soc/samsung/exynos-chipid.c
@@ -15,7 +15,9 @@
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/mfd/syscon.h>
+#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
@@ -24,6 +26,17 @@
#include "exynos-asv.h"
+struct exynos_chipid_variant {
+ unsigned int rev_reg; /* revision register offset */
+ unsigned int main_rev_shift; /* main revision offset in rev_reg */
+ unsigned int sub_rev_shift; /* sub revision offset in rev_reg */
+};
+
+struct exynos_chipid_info {
+ u32 product_id;
+ u32 revision;
+};
+
static const struct exynos_soc_id {
const char *name;
unsigned int id;
@@ -42,6 +55,8 @@ static const struct exynos_soc_id {
{ "EXYNOS5440", 0xE5440000 },
{ "EXYNOS5800", 0xE5422000 },
{ "EXYNOS7420", 0xE7420000 },
+ { "EXYNOS850", 0xE3830000 },
+ { "EXYNOSAUTOV9", 0xAAA80000 },
};
static const char *product_id_to_soc_id(unsigned int product_id)
@@ -49,31 +64,57 @@ static const char *product_id_to_soc_id(unsigned int product_id)
int i;
for (i = 0; i < ARRAY_SIZE(soc_ids); i++)
- if ((product_id & EXYNOS_MASK) == soc_ids[i].id)
+ if (product_id == soc_ids[i].id)
return soc_ids[i].name;
return NULL;
}
+static int exynos_chipid_get_chipid_info(struct regmap *regmap,
+ const struct exynos_chipid_variant *data,
+ struct exynos_chipid_info *soc_info)
+{
+ int ret;
+ unsigned int val, main_rev, sub_rev;
+
+ ret = regmap_read(regmap, EXYNOS_CHIPID_REG_PRO_ID, &val);
+ if (ret < 0)
+ return ret;
+ soc_info->product_id = val & EXYNOS_MASK;
+
+ if (data->rev_reg != EXYNOS_CHIPID_REG_PRO_ID) {
+ ret = regmap_read(regmap, data->rev_reg, &val);
+ if (ret < 0)
+ return ret;
+ }
+ main_rev = (val >> data->main_rev_shift) & EXYNOS_REV_PART_MASK;
+ sub_rev = (val >> data->sub_rev_shift) & EXYNOS_REV_PART_MASK;
+ soc_info->revision = (main_rev << EXYNOS_REV_PART_SHIFT) | sub_rev;
+
+ return 0;
+}
+
static int exynos_chipid_probe(struct platform_device *pdev)
{
+ const struct exynos_chipid_variant *drv_data;
+ struct exynos_chipid_info soc_info;
struct soc_device_attribute *soc_dev_attr;
struct soc_device *soc_dev;
struct device_node *root;
struct regmap *regmap;
- u32 product_id;
- u32 revision;
int ret;
+ drv_data = of_device_get_match_data(&pdev->dev);
+ if (!drv_data)
+ return -EINVAL;
+
regmap = device_node_to_regmap(pdev->dev.of_node);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
- ret = regmap_read(regmap, EXYNOS_CHIPID_REG_PRO_ID, &product_id);
+ ret = exynos_chipid_get_chipid_info(regmap, drv_data, &soc_info);
if (ret < 0)
return ret;
- revision = product_id & EXYNOS_REV_MASK;
-
soc_dev_attr = devm_kzalloc(&pdev->dev, sizeof(*soc_dev_attr),
GFP_KERNEL);
if (!soc_dev_attr)
@@ -86,8 +127,8 @@ static int exynos_chipid_probe(struct platform_device *pdev)
of_node_put(root);
soc_dev_attr->revision = devm_kasprintf(&pdev->dev, GFP_KERNEL,
- "%x", revision);
- soc_dev_attr->soc_id = product_id_to_soc_id(product_id);
+ "%x", soc_info.revision);
+ soc_dev_attr->soc_id = product_id_to_soc_id(soc_info.product_id);
if (!soc_dev_attr->soc_id) {
pr_err("Unknown SoC\n");
return -ENODEV;
@@ -104,9 +145,8 @@ static int exynos_chipid_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, soc_dev);
- dev_info(soc_device_to_device(soc_dev),
- "Exynos: CPU[%s] PRO_ID[0x%x] REV[0x%x] Detected\n",
- soc_dev_attr->soc_id, product_id, revision);
+ dev_info(&pdev->dev, "Exynos: CPU[%s] PRO_ID[0x%x] REV[0x%x] Detected\n",
+ soc_dev_attr->soc_id, soc_info.product_id, soc_info.revision);
return 0;
@@ -125,10 +165,29 @@ static int exynos_chipid_remove(struct platform_device *pdev)
return 0;
}
+static const struct exynos_chipid_variant exynos4210_chipid_drv_data = {
+ .rev_reg = 0x0,
+ .main_rev_shift = 4,
+ .sub_rev_shift = 0,
+};
+
+static const struct exynos_chipid_variant exynos850_chipid_drv_data = {
+ .rev_reg = 0x10,
+ .main_rev_shift = 20,
+ .sub_rev_shift = 16,
+};
+
static const struct of_device_id exynos_chipid_of_device_ids[] = {
- { .compatible = "samsung,exynos4210-chipid" },
- {}
+ {
+ .compatible = "samsung,exynos4210-chipid",
+ .data = &exynos4210_chipid_drv_data,
+ }, {
+ .compatible = "samsung,exynos850-chipid",
+ .data = &exynos850_chipid_drv_data,
+ },
+ { }
};
+MODULE_DEVICE_TABLE(of, exynos_chipid_of_device_ids);
static struct platform_driver exynos_chipid_driver = {
.driver = {
@@ -138,4 +197,11 @@ static struct platform_driver exynos_chipid_driver = {
.probe = exynos_chipid_probe,
.remove = exynos_chipid_remove,
};
-builtin_platform_driver(exynos_chipid_driver);
+module_platform_driver(exynos_chipid_driver);
+
+MODULE_DESCRIPTION("Samsung Exynos ChipID controller and ASV driver");
+MODULE_AUTHOR("Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>");
+MODULE_AUTHOR("Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com>");
+MODULE_AUTHOR("Pankaj Dubey <pankaj.dubey@samsung.com>");
+MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/soc/samsung/exynos5422-asv.c b/drivers/soc/samsung/exynos5422-asv.c
index ca409a976e34..475ae5276529 100644
--- a/drivers/soc/samsung/exynos5422-asv.c
+++ b/drivers/soc/samsung/exynos5422-asv.c
@@ -503,3 +503,4 @@ int exynos5422_asv_init(struct exynos_asv *asv)
return 0;
}
+EXPORT_SYMBOL_GPL(exynos5422_asv_init);
diff --git a/drivers/soc/samsung/pm_domains.c b/drivers/soc/samsung/pm_domains.c
index 5ec0c13f0aaf..d07f3c9d6903 100644
--- a/drivers/soc/samsung/pm_domains.c
+++ b/drivers/soc/samsung/pm_domains.c
@@ -28,7 +28,6 @@ struct exynos_pm_domain_config {
*/
struct exynos_pm_domain {
void __iomem *base;
- bool is_off;
struct generic_pm_domain pd;
u32 local_pwr_cfg;
};
diff --git a/drivers/soc/sunxi/sunxi_sram.c b/drivers/soc/sunxi/sunxi_sram.c
index 42833e33a96c..a8f3876963a0 100644
--- a/drivers/soc/sunxi/sunxi_sram.c
+++ b/drivers/soc/sunxi/sunxi_sram.c
@@ -331,7 +331,6 @@ static struct regmap_config sunxi_sram_emac_clock_regmap = {
static int sunxi_sram_probe(struct platform_device *pdev)
{
- struct resource *res;
struct dentry *d;
struct regmap *emac_clock;
const struct sunxi_sramc_variant *variant;
@@ -342,8 +341,7 @@ static int sunxi_sram_probe(struct platform_device *pdev)
if (!variant)
return -EINVAL;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(&pdev->dev, res);
+ base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
diff --git a/drivers/soc/tegra/Makefile b/drivers/soc/tegra/Makefile
index 9c809c1814bd..054e862b63d8 100644
--- a/drivers/soc/tegra/Makefile
+++ b/drivers/soc/tegra/Makefile
@@ -7,3 +7,4 @@ obj-$(CONFIG_SOC_TEGRA_PMC) += pmc.o
obj-$(CONFIG_SOC_TEGRA_POWERGATE_BPMP) += powergate-bpmp.o
obj-$(CONFIG_SOC_TEGRA20_VOLTAGE_COUPLER) += regulators-tegra20.o
obj-$(CONFIG_SOC_TEGRA30_VOLTAGE_COUPLER) += regulators-tegra30.o
+obj-$(CONFIG_ARCH_TEGRA_186_SOC) += ari-tegra186.o
diff --git a/drivers/soc/tegra/ari-tegra186.c b/drivers/soc/tegra/ari-tegra186.c
new file mode 100644
index 000000000000..02577853ec49
--- /dev/null
+++ b/drivers/soc/tegra/ari-tegra186.c
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved.
+ */
+
+#include <linux/arm-smccc.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/panic_notifier.h>
+
+#define SMC_SIP_INVOKE_MCE 0xc2ffff00
+#define MCE_SMC_READ_MCA 12
+
+#define MCA_ARI_CMD_RD_SERR 1
+
+#define MCA_ARI_RW_SUBIDX_STAT 1
+#define SERR_STATUS_VAL BIT_ULL(63)
+
+#define MCA_ARI_RW_SUBIDX_ADDR 2
+#define MCA_ARI_RW_SUBIDX_MSC1 3
+#define MCA_ARI_RW_SUBIDX_MSC2 4
+
+static const char * const bank_names[] = {
+ "SYS:DPMU", "ROC:IOB", "ROC:MCB", "ROC:CCE", "ROC:CQX", "ROC:CTU",
+};
+
+static void read_uncore_mca(u8 cmd, u8 idx, u8 subidx, u8 inst, u64 *data)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_smc(SMC_SIP_INVOKE_MCE | MCE_SMC_READ_MCA,
+ ((u64)inst << 24) | ((u64)idx << 16) |
+ ((u64)subidx << 8) | ((u64)cmd << 0),
+ 0, 0, 0, 0, 0, 0, &res);
+
+ *data = res.a2;
+}
+
+static int tegra186_ari_panic_handler(struct notifier_block *nb,
+ unsigned long code, void *unused)
+{
+ u64 status;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(bank_names); i++) {
+ read_uncore_mca(MCA_ARI_CMD_RD_SERR, i, MCA_ARI_RW_SUBIDX_STAT,
+ 0, &status);
+
+ if (status & SERR_STATUS_VAL) {
+ u64 addr, misc1, misc2;
+
+ read_uncore_mca(MCA_ARI_CMD_RD_SERR, i,
+ MCA_ARI_RW_SUBIDX_ADDR, 0, &addr);
+ read_uncore_mca(MCA_ARI_CMD_RD_SERR, i,
+ MCA_ARI_RW_SUBIDX_MSC1, 0, &misc1);
+ read_uncore_mca(MCA_ARI_CMD_RD_SERR, i,
+ MCA_ARI_RW_SUBIDX_MSC2, 0, &misc2);
+
+ pr_crit("Machine Check Error in %s\n"
+ " status=0x%llx addr=0x%llx\n"
+ " msc1=0x%llx msc2=0x%llx\n",
+ bank_names[i], status, addr, misc1, misc2);
+ }
+ }
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block tegra186_ari_panic_nb = {
+ .notifier_call = tegra186_ari_panic_handler,
+};
+
+static int __init tegra186_ari_init(void)
+{
+ if (of_machine_is_compatible("nvidia,tegra186"))
+ atomic_notifier_chain_register(&panic_notifier_list, &tegra186_ari_panic_nb);
+
+ return 0;
+}
+early_initcall(tegra186_ari_init);
diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
index 50091c4ec948..575d6d5b4294 100644
--- a/drivers/soc/tegra/pmc.c
+++ b/drivers/soc/tegra/pmc.c
@@ -360,6 +360,7 @@ struct tegra_pmc_soc {
unsigned int num_pmc_clks;
bool has_blink_output;
bool has_usb_sleepwalk;
+ bool supports_core_domain;
};
/**
@@ -782,7 +783,7 @@ static int tegra_powergate_power_up(struct tegra_powergate *pg,
err = reset_control_deassert(pg->reset);
if (err)
- goto powergate_off;
+ goto disable_clks;
usleep_range(10, 20);
@@ -2815,8 +2816,7 @@ static int tegra_pmc_probe(struct platform_device *pdev)
return err;
/* take over the memory region from the early initialization */
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(&pdev->dev, res);
+ base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
@@ -3041,6 +3041,7 @@ static void tegra20_pmc_setup_irq_polarity(struct tegra_pmc *pmc,
}
static const struct tegra_pmc_soc tegra20_pmc_soc = {
+ .supports_core_domain = false,
.num_powergates = ARRAY_SIZE(tegra20_powergates),
.powergates = tegra20_powergates,
.num_cpu_powergates = 0,
@@ -3065,7 +3066,7 @@ static const struct tegra_pmc_soc tegra20_pmc_soc = {
.pmc_clks_data = NULL,
.num_pmc_clks = 0,
.has_blink_output = true,
- .has_usb_sleepwalk = false,
+ .has_usb_sleepwalk = true,
};
static const char * const tegra30_powergates[] = {
@@ -3101,6 +3102,7 @@ static const char * const tegra30_reset_sources[] = {
};
static const struct tegra_pmc_soc tegra30_pmc_soc = {
+ .supports_core_domain = false,
.num_powergates = ARRAY_SIZE(tegra30_powergates),
.powergates = tegra30_powergates,
.num_cpu_powergates = ARRAY_SIZE(tegra30_cpu_powergates),
@@ -3125,7 +3127,7 @@ static const struct tegra_pmc_soc tegra30_pmc_soc = {
.pmc_clks_data = tegra_pmc_clks_data,
.num_pmc_clks = ARRAY_SIZE(tegra_pmc_clks_data),
.has_blink_output = true,
- .has_usb_sleepwalk = false,
+ .has_usb_sleepwalk = true,
};
static const char * const tegra114_powergates[] = {
@@ -3157,6 +3159,7 @@ static const u8 tegra114_cpu_powergates[] = {
};
static const struct tegra_pmc_soc tegra114_pmc_soc = {
+ .supports_core_domain = false,
.num_powergates = ARRAY_SIZE(tegra114_powergates),
.powergates = tegra114_powergates,
.num_cpu_powergates = ARRAY_SIZE(tegra114_cpu_powergates),
@@ -3181,7 +3184,7 @@ static const struct tegra_pmc_soc tegra114_pmc_soc = {
.pmc_clks_data = tegra_pmc_clks_data,
.num_pmc_clks = ARRAY_SIZE(tegra_pmc_clks_data),
.has_blink_output = true,
- .has_usb_sleepwalk = false,
+ .has_usb_sleepwalk = true,
};
static const char * const tegra124_powergates[] = {
@@ -3273,6 +3276,7 @@ static const struct pinctrl_pin_desc tegra124_pin_descs[] = {
};
static const struct tegra_pmc_soc tegra124_pmc_soc = {
+ .supports_core_domain = false,
.num_powergates = ARRAY_SIZE(tegra124_powergates),
.powergates = tegra124_powergates,
.num_cpu_powergates = ARRAY_SIZE(tegra124_cpu_powergates),
@@ -3398,6 +3402,7 @@ static const struct tegra_wake_event tegra210_wake_events[] = {
};
static const struct tegra_pmc_soc tegra210_pmc_soc = {
+ .supports_core_domain = false,
.num_powergates = ARRAY_SIZE(tegra210_powergates),
.powergates = tegra210_powergates,
.num_cpu_powergates = ARRAY_SIZE(tegra210_cpu_powergates),
@@ -3555,6 +3560,7 @@ static const struct tegra_wake_event tegra186_wake_events[] = {
};
static const struct tegra_pmc_soc tegra186_pmc_soc = {
+ .supports_core_domain = false,
.num_powergates = 0,
.powergates = NULL,
.num_cpu_powergates = 0,
@@ -3689,6 +3695,7 @@ static const struct tegra_wake_event tegra194_wake_events[] = {
};
static const struct tegra_pmc_soc tegra194_pmc_soc = {
+ .supports_core_domain = false,
.num_powergates = 0,
.powergates = NULL,
.num_cpu_powergates = 0,
@@ -3757,6 +3764,7 @@ static const char * const tegra234_reset_sources[] = {
};
static const struct tegra_pmc_soc tegra234_pmc_soc = {
+ .supports_core_domain = false,
.num_powergates = 0,
.powergates = NULL,
.num_cpu_powergates = 0,
@@ -3804,6 +3812,14 @@ static void tegra_pmc_sync_state(struct device *dev)
int err;
/*
+ * Newer device-trees have power domains, but we need to prepare all
+ * device drivers with runtime PM and OPP support first, otherwise
+ * state syncing is unsafe.
+ */
+ if (!pmc->soc->supports_core_domain)
+ return;
+
+ /*
* Older device-trees don't have core PD, and thus, there are
* no dependencies that will block the state syncing. We shouldn't
* mark the domain as synced in this case.