summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2017-07-04 14:47:47 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2017-07-04 14:47:47 -0700
commite8547112910540afb71589ee807ae6a4259f9755 (patch)
tree7d3d7eb17be4e6d2b181bffc4d471e93063674b4 /drivers
parent9ce32ac8f83729aca9f45ce9598dbc5451d1044b (diff)
parentffe3744a591fdce695da6b891378261e2caedc69 (diff)
downloadop-kernel-dev-e8547112910540afb71589ee807ae6a4259f9755.zip
op-kernel-dev-e8547112910540afb71589ee807ae6a4259f9755.tar.gz
Merge tag 'armsoc-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
Pull ARM SoC driver updates from Arnd Bergmann: "New SoC specific drivers: - NVIDIA Tegra PM Domain support for newer SoCs (Tegra186 and later) based on the "BPMP" firmware - Clocksource and system controller drivers for the newly added Action Semi platforms (both arm and arm64). Reset subsystem, merged through arm-soc by tradition: - New drivers for Altera Stratix10, TI Keystone and Cortina Gemini SoCs - Various subsystem-wide cleanups Updates for existing SoC-specific drivers - TI GPMC (General Purpose Memory Controller) - Mediatek "scpsys" system controller support for MT6797 - Broadcom "brcmstb_gisb" bus arbitrer - ARM SCPI firmware - Renesas "SYSC" system controller One more driver update was submitted for the Freescale/NXP DPAA data path acceleration that has previously been used on PowerPC chips. I ended up postponing the merge until some API questions for its unusual MMIO access are resolved" * tag 'armsoc-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (35 commits) clocksource: owl: Add S900 support clocksource: Add Owl timer soc: renesas: rcar-sysc: Use GENPD_FLAG_ALWAYS_ON firmware: tegra: Fix locking bugs in BPMP soc/tegra: flowctrl: Fix error handling soc/tegra: bpmp: Implement generic PM domains soc/tegra: bpmp: Update ABI header PM / Domains: Allow overriding the ->xlate() callback soc: brcmstb: enable drivers for ARM64 and BMIPS soc: renesas: Rework Kconfig and Makefile logic reset: Add the TI SCI reset driver dt-bindings: reset: Add TI SCI reset binding reset: use kref for reference counting soc: qcom: smsm: Improve error handling, quiesce probe deferral cpufreq: scpi: use new scpi_ops functions to remove duplicate code firmware: arm_scpi: add support to populate OPPs and get transition latency dt-bindings: reset: Add reset manager offsets for Stratix10 memory: omap-gpmc: add error message if bank-width property is absent memory: omap-gpmc: make dts snippet include semicolon reset: Add a Gemini reset controller ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/base/power/domain.c8
-rw-r--r--drivers/bus/Kconfig2
-rw-r--r--drivers/bus/brcmstb_gisb.c121
-rw-r--r--drivers/clocksource/Kconfig7
-rw-r--r--drivers/clocksource/Makefile1
-rw-r--r--drivers/clocksource/owl-timer.c172
-rw-r--r--drivers/cpufreq/scpi-cpufreq.c38
-rw-r--r--drivers/firmware/arm_scpi.c63
-rw-r--r--drivers/firmware/tegra/bpmp.c32
-rw-r--r--drivers/memory/omap-gpmc.c9
-rw-r--r--drivers/reset/Kconfig17
-rw-r--r--drivers/reset/Makefile4
-rw-r--r--drivers/reset/core.c23
-rw-r--r--drivers/reset/reset-gemini.c110
-rw-r--r--drivers/reset/reset-ti-sci.c269
-rw-r--r--drivers/reset/sti/reset-syscfg.c6
-rw-r--r--drivers/soc/Kconfig1
-rw-r--r--drivers/soc/Makefile2
-rw-r--r--drivers/soc/bcm/Kconfig2
-rw-r--r--drivers/soc/imx/Makefile2
-rw-r--r--drivers/soc/mediatek/mtk-pmic-wrap.c14
-rw-r--r--drivers/soc/mediatek/mtk-scpsys.c149
-rw-r--r--drivers/soc/qcom/smsm.c9
-rw-r--r--drivers/soc/renesas/Kconfig63
-rw-r--r--drivers/soc/renesas/Makefile31
-rw-r--r--drivers/soc/renesas/rcar-sysc.c52
-rw-r--r--drivers/soc/renesas/rcar-sysc.h2
-rw-r--r--drivers/soc/tegra/Kconfig5
-rw-r--r--drivers/soc/tegra/Makefile1
-rw-r--r--drivers/soc/tegra/flowctrl.c2
-rw-r--r--drivers/soc/tegra/powergate-bpmp.c359
31 files changed, 1391 insertions, 185 deletions
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index b8e4b96..9649dce 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -1637,9 +1637,6 @@ EXPORT_SYMBOL_GPL(pm_genpd_remove);
#ifdef CONFIG_PM_GENERIC_DOMAINS_OF
-typedef struct generic_pm_domain *(*genpd_xlate_t)(struct of_phandle_args *args,
- void *data);
-
/*
* Device Tree based PM domain providers.
*
@@ -1795,6 +1792,9 @@ int of_genpd_add_provider_onecell(struct device_node *np,
mutex_lock(&gpd_list_lock);
+ if (!data->xlate)
+ data->xlate = genpd_xlate_onecell;
+
for (i = 0; i < data->num_domains; i++) {
if (!data->domains[i])
continue;
@@ -1805,7 +1805,7 @@ int of_genpd_add_provider_onecell(struct device_node *np,
data->domains[i]->has_provider = true;
}
- ret = genpd_add_provider(np, genpd_xlate_onecell, data);
+ ret = genpd_add_provider(np, data->xlate, data);
if (ret < 0)
goto error;
diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig
index 0a52da4..d2a5f11 100644
--- a/drivers/bus/Kconfig
+++ b/drivers/bus/Kconfig
@@ -57,7 +57,7 @@ config ARM_CCN
config BRCMSTB_GISB_ARB
bool "Broadcom STB GISB bus arbiter"
- depends on ARM || MIPS
+ depends on ARM || ARM64 || MIPS
default ARCH_BRCMSTB || BMIPS_GENERIC
help
Driver for the Broadcom Set Top Box System-on-a-chip internal bus
diff --git a/drivers/bus/brcmstb_gisb.c b/drivers/bus/brcmstb_gisb.c
index 72fe0a5..68ac3e9 100644
--- a/drivers/bus/brcmstb_gisb.c
+++ b/drivers/bus/brcmstb_gisb.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 Broadcom Corporation
+ * Copyright (C) 2014-2017 Broadcom
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -24,11 +24,9 @@
#include <linux/of.h>
#include <linux/bitops.h>
#include <linux/pm.h>
-
-#ifdef CONFIG_ARM
-#include <asm/bug.h>
-#include <asm/signal.h>
-#endif
+#include <linux/kernel.h>
+#include <linux/kdebug.h>
+#include <linux/notifier.h>
#ifdef CONFIG_MIPS
#include <asm/traps.h>
@@ -37,8 +35,6 @@
#define ARB_ERR_CAP_CLEAR (1 << 0)
#define ARB_ERR_CAP_STATUS_TIMEOUT (1 << 12)
#define ARB_ERR_CAP_STATUS_TEA (1 << 11)
-#define ARB_ERR_CAP_STATUS_BS_SHIFT (1 << 2)
-#define ARB_ERR_CAP_STATUS_BS_MASK 0x3c
#define ARB_ERR_CAP_STATUS_WRITE (1 << 1)
#define ARB_ERR_CAP_STATUS_VALID (1 << 0)
@@ -47,7 +43,6 @@ enum {
ARB_ERR_CAP_CLR,
ARB_ERR_CAP_HI_ADDR,
ARB_ERR_CAP_ADDR,
- ARB_ERR_CAP_DATA,
ARB_ERR_CAP_STATUS,
ARB_ERR_CAP_MASTER,
};
@@ -57,17 +52,24 @@ static const int gisb_offsets_bcm7038[] = {
[ARB_ERR_CAP_CLR] = 0x0c4,
[ARB_ERR_CAP_HI_ADDR] = -1,
[ARB_ERR_CAP_ADDR] = 0x0c8,
- [ARB_ERR_CAP_DATA] = 0x0cc,
[ARB_ERR_CAP_STATUS] = 0x0d0,
[ARB_ERR_CAP_MASTER] = -1,
};
+static const int gisb_offsets_bcm7278[] = {
+ [ARB_TIMER] = 0x008,
+ [ARB_ERR_CAP_CLR] = 0x7f8,
+ [ARB_ERR_CAP_HI_ADDR] = -1,
+ [ARB_ERR_CAP_ADDR] = 0x7e0,
+ [ARB_ERR_CAP_STATUS] = 0x7f0,
+ [ARB_ERR_CAP_MASTER] = 0x7f4,
+};
+
static const int gisb_offsets_bcm7400[] = {
[ARB_TIMER] = 0x00c,
[ARB_ERR_CAP_CLR] = 0x0c8,
[ARB_ERR_CAP_HI_ADDR] = -1,
[ARB_ERR_CAP_ADDR] = 0x0cc,
- [ARB_ERR_CAP_DATA] = 0x0d0,
[ARB_ERR_CAP_STATUS] = 0x0d4,
[ARB_ERR_CAP_MASTER] = 0x0d8,
};
@@ -77,7 +79,6 @@ static const int gisb_offsets_bcm7435[] = {
[ARB_ERR_CAP_CLR] = 0x168,
[ARB_ERR_CAP_HI_ADDR] = -1,
[ARB_ERR_CAP_ADDR] = 0x16c,
- [ARB_ERR_CAP_DATA] = 0x170,
[ARB_ERR_CAP_STATUS] = 0x174,
[ARB_ERR_CAP_MASTER] = 0x178,
};
@@ -87,7 +88,6 @@ static const int gisb_offsets_bcm7445[] = {
[ARB_ERR_CAP_CLR] = 0x7e4,
[ARB_ERR_CAP_HI_ADDR] = 0x7e8,
[ARB_ERR_CAP_ADDR] = 0x7ec,
- [ARB_ERR_CAP_DATA] = 0x7f0,
[ARB_ERR_CAP_STATUS] = 0x7f4,
[ARB_ERR_CAP_MASTER] = 0x7f8,
};
@@ -109,9 +109,13 @@ static u32 gisb_read(struct brcmstb_gisb_arb_device *gdev, int reg)
{
int offset = gdev->gisb_offsets[reg];
- /* return 1 if the hardware doesn't have ARB_ERR_CAP_MASTER */
- if (offset == -1)
- return 1;
+ if (offset < 0) {
+ /* return 1 if the hardware doesn't have ARB_ERR_CAP_MASTER */
+ if (reg == ARB_ERR_CAP_MASTER)
+ return 1;
+ else
+ return 0;
+ }
if (gdev->big_endian)
return ioread32be(gdev->base + offset);
@@ -119,6 +123,16 @@ static u32 gisb_read(struct brcmstb_gisb_arb_device *gdev, int reg)
return ioread32(gdev->base + offset);
}
+static u64 gisb_read_address(struct brcmstb_gisb_arb_device *gdev)
+{
+ u64 value;
+
+ value = gisb_read(gdev, ARB_ERR_CAP_ADDR);
+ value |= (u64)gisb_read(gdev, ARB_ERR_CAP_HI_ADDR) << 32;
+
+ return value;
+}
+
static void gisb_write(struct brcmstb_gisb_arb_device *gdev, u32 val, int reg)
{
int offset = gdev->gisb_offsets[reg];
@@ -127,9 +141,9 @@ static void gisb_write(struct brcmstb_gisb_arb_device *gdev, u32 val, int reg)
return;
if (gdev->big_endian)
- iowrite32be(val, gdev->base + reg);
+ iowrite32be(val, gdev->base + offset);
else
- iowrite32(val, gdev->base + reg);
+ iowrite32(val, gdev->base + offset);
}
static ssize_t gisb_arb_get_timeout(struct device *dev,
@@ -185,7 +199,7 @@ static int brcmstb_gisb_arb_decode_addr(struct brcmstb_gisb_arb_device *gdev,
const char *reason)
{
u32 cap_status;
- unsigned long arb_addr;
+ u64 arb_addr;
u32 master;
const char *m_name;
char m_fmt[11];
@@ -197,10 +211,7 @@ static int brcmstb_gisb_arb_decode_addr(struct brcmstb_gisb_arb_device *gdev,
return 1;
/* Read the address and master */
- arb_addr = gisb_read(gdev, ARB_ERR_CAP_ADDR) & 0xffffffff;
-#if (IS_ENABLED(CONFIG_PHYS_ADDR_T_64BIT))
- arb_addr |= (u64)gisb_read(gdev, ARB_ERR_CAP_HI_ADDR) << 32;
-#endif
+ arb_addr = gisb_read_address(gdev);
master = gisb_read(gdev, ARB_ERR_CAP_MASTER);
m_name = brcmstb_gisb_master_to_str(gdev, master);
@@ -209,7 +220,7 @@ static int brcmstb_gisb_arb_decode_addr(struct brcmstb_gisb_arb_device *gdev,
m_name = m_fmt;
}
- pr_crit("%s: %s at 0x%lx [%c %s], core: %s\n",
+ pr_crit("%s: %s at 0x%llx [%c %s], core: %s\n",
__func__, reason, arb_addr,
cap_status & ARB_ERR_CAP_STATUS_WRITE ? 'W' : 'R',
cap_status & ARB_ERR_CAP_STATUS_TIMEOUT ? "timeout" : "",
@@ -221,27 +232,6 @@ static int brcmstb_gisb_arb_decode_addr(struct brcmstb_gisb_arb_device *gdev,
return 0;
}
-#ifdef CONFIG_ARM
-static int brcmstb_bus_error_handler(unsigned long addr, unsigned int fsr,
- struct pt_regs *regs)
-{
- int ret = 0;
- struct brcmstb_gisb_arb_device *gdev;
-
- /* iterate over each GISB arb registered handlers */
- list_for_each_entry(gdev, &brcmstb_gisb_arb_device_list, next)
- ret |= brcmstb_gisb_arb_decode_addr(gdev, "bus error");
- /*
- * If it was an imprecise abort, then we need to correct the
- * return address to be _after_ the instruction.
- */
- if (fsr & (1 << 10))
- regs->ARM_pc += 4;
-
- return ret;
-}
-#endif
-
#ifdef CONFIG_MIPS
static int brcmstb_bus_error_handler(struct pt_regs *regs, int is_fixup)
{
@@ -279,6 +269,36 @@ static irqreturn_t brcmstb_gisb_tea_handler(int irq, void *dev_id)
return IRQ_HANDLED;
}
+/*
+ * Dump out gisb errors on die or panic.
+ */
+static int dump_gisb_error(struct notifier_block *self, unsigned long v,
+ void *p);
+
+static struct notifier_block gisb_die_notifier = {
+ .notifier_call = dump_gisb_error,
+};
+
+static struct notifier_block gisb_panic_notifier = {
+ .notifier_call = dump_gisb_error,
+};
+
+static int dump_gisb_error(struct notifier_block *self, unsigned long v,
+ void *p)
+{
+ struct brcmstb_gisb_arb_device *gdev;
+ const char *reason = "panic";
+
+ if (self == &gisb_die_notifier)
+ reason = "die";
+
+ /* iterate over each GISB arb registered handlers */
+ list_for_each_entry(gdev, &brcmstb_gisb_arb_device_list, next)
+ brcmstb_gisb_arb_decode_addr(gdev, reason);
+
+ return NOTIFY_DONE;
+}
+
static DEVICE_ATTR(gisb_arb_timeout, S_IWUSR | S_IRUGO,
gisb_arb_get_timeout, gisb_arb_set_timeout);
@@ -296,6 +316,7 @@ static const struct of_device_id brcmstb_gisb_arb_of_match[] = {
{ .compatible = "brcm,bcm7445-gisb-arb", .data = gisb_offsets_bcm7445 },
{ .compatible = "brcm,bcm7435-gisb-arb", .data = gisb_offsets_bcm7435 },
{ .compatible = "brcm,bcm7400-gisb-arb", .data = gisb_offsets_bcm7400 },
+ { .compatible = "brcm,bcm7278-gisb-arb", .data = gisb_offsets_bcm7278 },
{ .compatible = "brcm,bcm7038-gisb-arb", .data = gisb_offsets_bcm7038 },
{ },
};
@@ -378,14 +399,16 @@ static int __init brcmstb_gisb_arb_probe(struct platform_device *pdev)
list_add_tail(&gdev->next, &brcmstb_gisb_arb_device_list);
-#ifdef CONFIG_ARM
- hook_fault_code(22, brcmstb_bus_error_handler, SIGBUS, 0,
- "imprecise external abort");
-#endif
#ifdef CONFIG_MIPS
board_be_handler = brcmstb_bus_error_handler;
#endif
+ if (list_is_singular(&brcmstb_gisb_arb_device_list)) {
+ register_die_notifier(&gisb_die_notifier);
+ atomic_notifier_chain_register(&panic_notifier_list,
+ &gisb_panic_notifier);
+ }
+
dev_info(&pdev->dev, "registered mem: %p, irqs: %d, %d\n",
gdev->base, timeout_irq, tea_irq);
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 88818a4..fcae5ca 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -103,6 +103,13 @@ config ORION_TIMER
help
Enables the support for the Orion timer driver
+config OWL_TIMER
+ bool "Owl timer driver" if COMPILE_TEST
+ depends on GENERIC_CLOCKEVENTS
+ select CLKSRC_MMIO
+ help
+ Enables the support for the Actions Semi Owl timer driver.
+
config SUN4I_TIMER
bool "Sun4i timer driver" if COMPILE_TEST
depends on GENERIC_CLOCKEVENTS
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 72bfd00..6df9494 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -52,6 +52,7 @@ obj-$(CONFIG_CLKSRC_PISTACHIO) += time-pistachio.o
obj-$(CONFIG_CLKSRC_TI_32K) += timer-ti-32k.o
obj-$(CONFIG_CLKSRC_NPS) += timer-nps.o
obj-$(CONFIG_OXNAS_RPS_TIMER) += timer-oxnas-rps.o
+obj-$(CONFIG_OWL_TIMER) += owl-timer.o
obj-$(CONFIG_ARC_TIMERS) += arc_timer.o
obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o
diff --git a/drivers/clocksource/owl-timer.c b/drivers/clocksource/owl-timer.c
new file mode 100644
index 0000000..d19c53c
--- /dev/null
+++ b/drivers/clocksource/owl-timer.c
@@ -0,0 +1,172 @@
+/*
+ * Actions Semi Owl timer
+ *
+ * Copyright 2012 Actions Semi Inc.
+ * Author: Actions Semi, Inc.
+ *
+ * Copyright (c) 2017 SUSE Linux GmbH
+ * Author: Andreas Färber
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/clockchips.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqreturn.h>
+#include <linux/sched_clock.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+
+#define OWL_Tx_CTL 0x0
+#define OWL_Tx_CMP 0x4
+#define OWL_Tx_VAL 0x8
+
+#define OWL_Tx_CTL_PD BIT(0)
+#define OWL_Tx_CTL_INTEN BIT(1)
+#define OWL_Tx_CTL_EN BIT(2)
+
+static void __iomem *owl_timer_base;
+static void __iomem *owl_clksrc_base;
+static void __iomem *owl_clkevt_base;
+
+static inline void owl_timer_reset(void __iomem *base)
+{
+ writel(0, base + OWL_Tx_CTL);
+ writel(0, base + OWL_Tx_VAL);
+ writel(0, base + OWL_Tx_CMP);
+}
+
+static inline void owl_timer_set_enabled(void __iomem *base, bool enabled)
+{
+ u32 ctl = readl(base + OWL_Tx_CTL);
+
+ /* PD bit is cleared when set */
+ ctl &= ~OWL_Tx_CTL_PD;
+
+ if (enabled)
+ ctl |= OWL_Tx_CTL_EN;
+ else
+ ctl &= ~OWL_Tx_CTL_EN;
+
+ writel(ctl, base + OWL_Tx_CTL);
+}
+
+static u64 notrace owl_timer_sched_read(void)
+{
+ return (u64)readl(owl_clksrc_base + OWL_Tx_VAL);
+}
+
+static int owl_timer_set_state_shutdown(struct clock_event_device *evt)
+{
+ owl_timer_set_enabled(owl_clkevt_base, false);
+
+ return 0;
+}
+
+static int owl_timer_set_state_oneshot(struct clock_event_device *evt)
+{
+ owl_timer_reset(owl_clkevt_base);
+
+ return 0;
+}
+
+static int owl_timer_tick_resume(struct clock_event_device *evt)
+{
+ return 0;
+}
+
+static int owl_timer_set_next_event(unsigned long evt,
+ struct clock_event_device *ev)
+{
+ void __iomem *base = owl_clkevt_base;
+
+ owl_timer_set_enabled(base, false);
+ writel(OWL_Tx_CTL_INTEN, base + OWL_Tx_CTL);
+ writel(0, base + OWL_Tx_VAL);
+ writel(evt, base + OWL_Tx_CMP);
+ owl_timer_set_enabled(base, true);
+
+ return 0;
+}
+
+static struct clock_event_device owl_clockevent = {
+ .name = "owl_tick",
+ .rating = 200,
+ .features = CLOCK_EVT_FEAT_ONESHOT |
+ CLOCK_EVT_FEAT_DYNIRQ,
+ .set_state_shutdown = owl_timer_set_state_shutdown,
+ .set_state_oneshot = owl_timer_set_state_oneshot,
+ .tick_resume = owl_timer_tick_resume,
+ .set_next_event = owl_timer_set_next_event,
+};
+
+static irqreturn_t owl_timer1_interrupt(int irq, void *dev_id)
+{
+ struct clock_event_device *evt = (struct clock_event_device *)dev_id;
+
+ writel(OWL_Tx_CTL_PD, owl_clkevt_base + OWL_Tx_CTL);
+
+ evt->event_handler(evt);
+
+ return IRQ_HANDLED;
+}
+
+static int __init owl_timer_init(struct device_node *node)
+{
+ struct clk *clk;
+ unsigned long rate;
+ int timer1_irq, ret;
+
+ owl_timer_base = of_io_request_and_map(node, 0, "owl-timer");
+ if (IS_ERR(owl_timer_base)) {
+ pr_err("Can't map timer registers");
+ return PTR_ERR(owl_timer_base);
+ }
+
+ owl_clksrc_base = owl_timer_base + 0x08;
+ owl_clkevt_base = owl_timer_base + 0x14;
+
+ timer1_irq = of_irq_get_byname(node, "timer1");
+ if (timer1_irq <= 0) {
+ pr_err("Can't parse timer1 IRQ");
+ return -EINVAL;
+ }
+
+ clk = of_clk_get(node, 0);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ rate = clk_get_rate(clk);
+
+ owl_timer_reset(owl_clksrc_base);
+ owl_timer_set_enabled(owl_clksrc_base, true);
+
+ sched_clock_register(owl_timer_sched_read, 32, rate);
+ clocksource_mmio_init(owl_clksrc_base + OWL_Tx_VAL, node->name,
+ rate, 200, 32, clocksource_mmio_readl_up);
+
+ owl_timer_reset(owl_clkevt_base);
+
+ ret = request_irq(timer1_irq, owl_timer1_interrupt, IRQF_TIMER,
+ "owl-timer", &owl_clockevent);
+ if (ret) {
+ pr_err("failed to request irq %d\n", timer1_irq);
+ return ret;
+ }
+
+ owl_clockevent.cpumask = cpumask_of(0);
+ owl_clockevent.irq = timer1_irq;
+
+ clockevents_config_and_register(&owl_clockevent, rate,
+ 0xf, 0xffffffff);
+
+ return 0;
+}
+CLOCKSOURCE_OF_DECLARE(owl_s500, "actions,s500-timer", owl_timer_init);
+CLOCKSOURCE_OF_DECLARE(owl_s900, "actions,s900-timer", owl_timer_init);
diff --git a/drivers/cpufreq/scpi-cpufreq.c b/drivers/cpufreq/scpi-cpufreq.c
index ea7a4e1..8de2364 100644
--- a/drivers/cpufreq/scpi-cpufreq.c
+++ b/drivers/cpufreq/scpi-cpufreq.c
@@ -30,46 +30,20 @@
static struct scpi_ops *scpi_ops;
-static struct scpi_dvfs_info *scpi_get_dvfs_info(struct device *cpu_dev)
-{
- int domain = topology_physical_package_id(cpu_dev->id);
-
- if (domain < 0)
- return ERR_PTR(-EINVAL);
- return scpi_ops->dvfs_get_info(domain);
-}
-
static int scpi_get_transition_latency(struct device *cpu_dev)
{
- struct scpi_dvfs_info *info = scpi_get_dvfs_info(cpu_dev);
-
- if (IS_ERR(info))
- return PTR_ERR(info);
- return info->latency;
+ return scpi_ops->get_transition_latency(cpu_dev);
}
static int scpi_init_opp_table(const struct cpumask *cpumask)
{
- int idx, ret;
- struct scpi_opp *opp;
+ int ret;
struct device *cpu_dev = get_cpu_device(cpumask_first(cpumask));
- struct scpi_dvfs_info *info = scpi_get_dvfs_info(cpu_dev);
-
- if (IS_ERR(info))
- return PTR_ERR(info);
-
- if (!info->opps)
- return -EIO;
- for (opp = info->opps, idx = 0; idx < info->count; idx++, opp++) {
- ret = dev_pm_opp_add(cpu_dev, opp->freq, opp->m_volt * 1000);
- if (ret) {
- dev_warn(cpu_dev, "failed to add opp %uHz %umV\n",
- opp->freq, opp->m_volt);
- while (idx-- > 0)
- dev_pm_opp_remove(cpu_dev, (--opp)->freq);
- return ret;
- }
+ ret = scpi_ops->add_opps_to_device(cpu_dev);
+ if (ret) {
+ dev_warn(cpu_dev, "failed to add opps to the device\n");
+ return ret;
}
ret = dev_pm_opp_set_sharing_cpus(cpu_dev, cpumask);
diff --git a/drivers/firmware/arm_scpi.c b/drivers/firmware/arm_scpi.c
index f6cfc31..8043e51 100644
--- a/drivers/firmware/arm_scpi.c
+++ b/drivers/firmware/arm_scpi.c
@@ -39,6 +39,7 @@
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/printk.h>
+#include <linux/pm_opp.h>
#include <linux/scpi_protocol.h>
#include <linux/slab.h>
#include <linux/sort.h>
@@ -684,6 +685,65 @@ static struct scpi_dvfs_info *scpi_dvfs_get_info(u8 domain)
return info;
}
+static int scpi_dev_domain_id(struct device *dev)
+{
+ struct of_phandle_args clkspec;
+
+ if (of_parse_phandle_with_args(dev->of_node, "clocks", "#clock-cells",
+ 0, &clkspec))
+ return -EINVAL;
+
+ return clkspec.args[0];
+}
+
+static struct scpi_dvfs_info *scpi_dvfs_info(struct device *dev)
+{
+ int domain = scpi_dev_domain_id(dev);
+
+ if (domain < 0)
+ return ERR_PTR(domain);
+
+ return scpi_dvfs_get_info(domain);
+}
+
+static int scpi_dvfs_get_transition_latency(struct device *dev)
+{
+ struct scpi_dvfs_info *info = scpi_dvfs_info(dev);
+
+ if (IS_ERR(info))
+ return PTR_ERR(info);
+
+ if (!info->latency)
+ return 0;
+
+ return info->latency;
+}
+
+static int scpi_dvfs_add_opps_to_device(struct device *dev)
+{
+ int idx, ret;
+ struct scpi_opp *opp;
+ struct scpi_dvfs_info *info = scpi_dvfs_info(dev);
+
+ if (IS_ERR(info))
+ return PTR_ERR(info);
+
+ if (!info->opps)
+ return -EIO;
+
+ for (opp = info->opps, idx = 0; idx < info->count; idx++, opp++) {
+ ret = dev_pm_opp_add(dev, opp->freq, opp->m_volt * 1000);
+ if (ret) {
+ dev_warn(dev, "failed to add opp %uHz %umV\n",
+ opp->freq, opp->m_volt);
+ while (idx-- > 0)
+ dev_pm_opp_remove(dev, (--opp)->freq);
+ return ret;
+ }
+ }
+ return 0;
+}
+
static int scpi_sensor_get_capability(u16 *sensors)
{
struct sensor_capabilities cap_buf;
@@ -765,6 +825,9 @@ static struct scpi_ops scpi_ops = {
.dvfs_get_idx = scpi_dvfs_get_idx,
.dvfs_set_idx = scpi_dvfs_set_idx,
.dvfs_get_info = scpi_dvfs_get_info,
+ .device_domain_id = scpi_dev_domain_id,
+ .get_transition_latency = scpi_dvfs_get_transition_latency,
+ .add_opps_to_device = scpi_dvfs_add_opps_to_device,
.sensor_get_capability = scpi_sensor_get_capability,
.sensor_get_info = scpi_sensor_get_info,
.sensor_get_value = scpi_sensor_get_value,
diff --git a/drivers/firmware/tegra/bpmp.c b/drivers/firmware/tegra/bpmp.c
index 84e4c9a..b251795 100644
--- a/drivers/firmware/tegra/bpmp.c
+++ b/drivers/firmware/tegra/bpmp.c
@@ -211,14 +211,17 @@ static ssize_t tegra_bpmp_channel_read(struct tegra_bpmp_channel *channel,
int index;
index = tegra_bpmp_channel_get_thread_index(channel);
- if (index < 0)
- return index;
+ if (index < 0) {
+ err = index;
+ goto unlock;
+ }
spin_lock_irqsave(&bpmp->lock, flags);
err = __tegra_bpmp_channel_read(channel, data, size);
clear_bit(index, bpmp->threaded.allocated);
spin_unlock_irqrestore(&bpmp->lock, flags);
+unlock:
up(&bpmp->threaded.lock);
return err;
@@ -256,18 +259,18 @@ tegra_bpmp_write_threaded(struct tegra_bpmp *bpmp, unsigned int mrq,
index = find_first_zero_bit(bpmp->threaded.allocated, count);
if (index == count) {
- channel = ERR_PTR(-EBUSY);
+ err = -EBUSY;
goto unlock;
}
channel = tegra_bpmp_channel_get_thread(bpmp, index);
if (!channel) {
- channel = ERR_PTR(-EINVAL);
+ err = -EINVAL;
goto unlock;
}
if (!tegra_bpmp_master_free(channel)) {
- channel = ERR_PTR(-EBUSY);
+ err = -EBUSY;
goto unlock;
}
@@ -275,16 +278,21 @@ tegra_bpmp_write_threaded(struct tegra_bpmp *bpmp, unsigned int mrq,
err = __tegra_bpmp_channel_write(channel, mrq, MSG_ACK | MSG_RING,
data, size);
- if (err < 0) {
- clear_bit(index, bpmp->threaded.allocated);
- goto unlock;
- }
+ if (err < 0)
+ goto clear_allocated;
set_bit(index, bpmp->threaded.busy);
-unlock:
spin_unlock_irqrestore(&bpmp->lock, flags);
return channel;
+
+clear_allocated:
+ clear_bit(index, bpmp->threaded.allocated);
+unlock:
+ spin_unlock_irqrestore(&bpmp->lock, flags);
+ up(&bpmp->threaded.lock);
+
+ return ERR_PTR(err);
}
static ssize_t tegra_bpmp_channel_write(struct tegra_bpmp_channel *channel,
@@ -810,6 +818,10 @@ static int tegra_bpmp_probe(struct platform_device *pdev)
if (err < 0)
goto free_mrq;
+ err = tegra_bpmp_init_powergates(bpmp);
+ if (err < 0)
+ goto free_mrq;
+
platform_set_drvdata(pdev, bpmp);
return 0;
diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c
index 6d1b4b7..a80e17d 100644
--- a/drivers/memory/omap-gpmc.c
+++ b/drivers/memory/omap-gpmc.c
@@ -460,12 +460,12 @@ static int get_gpmc_timing_reg(
if (l)
time_ns_min = gpmc_clk_ticks_to_ns(l - 1, cs, cd) + 1;
time_ns = gpmc_clk_ticks_to_ns(l, cs, cd);
- pr_info("gpmc,%s = <%u> /* %u ns - %u ns; %i ticks%s*/\n",
+ pr_info("gpmc,%s = <%u>; /* %u ns - %u ns; %i ticks%s*/\n",
name, time_ns, time_ns_min, time_ns, l,
invalid ? "; invalid " : " ");
} else {
/* raw format */
- pr_info("gpmc,%s = <%u>%s\n", name, l,
+ pr_info("gpmc,%s = <%u>;%s\n", name, l,
invalid ? " /* invalid */" : "");
}
@@ -2083,8 +2083,11 @@ static int gpmc_probe_generic_child(struct platform_device *pdev,
} else {
ret = of_property_read_u32(child, "bank-width",
&gpmc_s.device_width);
- if (ret < 0)
+ if (ret < 0) {
+ dev_err(&pdev->dev, "%s has no 'bank-width' property\n",
+ child->full_name);
goto err;
+ }
}
/* Reserve wait pin if it is required and valid */
diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index d21c07c..608c071 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -34,6 +34,13 @@ config RESET_BERLIN
help
This enables the reset controller driver for Marvell Berlin SoCs.
+config RESET_GEMINI
+ bool "Gemini Reset Driver" if COMPILE_TEST
+ default ARCH_GEMINI
+ select MFD_SYSCON
+ help
+ This enables the reset controller driver for Cortina Systems Gemini.
+
config RESET_IMX7
bool "i.MX7 Reset Driver" if COMPILE_TEST
default SOC_IMX7D
@@ -80,7 +87,15 @@ config RESET_SUNXI
help
This enables the reset driver for Allwinner SoCs.
-config TI_SYSCON_RESET
+config RESET_TI_SCI
+ tristate "TI System Control Interface (TI-SCI) reset driver"
+ depends on TI_SCI_PROTOCOL
+ help
+ This enables the reset driver support over TI System Control Interface
+ available on some new TI's SoCs. If you wish to use reset resources
+ managed by the TI System Controller, say Y here. Otherwise, say N.
+
+config RESET_TI_SYSCON
tristate "TI SYSCON Reset Driver"
depends on HAS_IOMEM
select MFD_SYSCON
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index 02a74db..7081f9d 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -5,6 +5,7 @@ obj-$(CONFIG_ARCH_TEGRA) += tegra/
obj-$(CONFIG_RESET_A10SR) += reset-a10sr.o
obj-$(CONFIG_RESET_ATH79) += reset-ath79.o
obj-$(CONFIG_RESET_BERLIN) += reset-berlin.o
+obj-$(CONFIG_RESET_GEMINI) += reset-gemini.o
obj-$(CONFIG_RESET_IMX7) += reset-imx7.o
obj-$(CONFIG_RESET_LPC18XX) += reset-lpc18xx.o
obj-$(CONFIG_RESET_MESON) += reset-meson.o
@@ -13,7 +14,8 @@ obj-$(CONFIG_RESET_PISTACHIO) += reset-pistachio.o
obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o
obj-$(CONFIG_RESET_STM32) += reset-stm32.o
obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o
-obj-$(CONFIG_TI_SYSCON_RESET) += reset-ti-syscon.o
+obj-$(CONFIG_RESET_TI_SCI) += reset-ti-sci.o
+obj-$(CONFIG_RESET_TI_SYSCON) += reset-ti-syscon.o
obj-$(CONFIG_RESET_UNIPHIER) += reset-uniphier.o
obj-$(CONFIG_RESET_ZX2967) += reset-zx2967.o
obj-$(CONFIG_RESET_ZYNQ) += reset-zynq.o
diff --git a/drivers/reset/core.c b/drivers/reset/core.c
index cd739d2..0090784 100644
--- a/drivers/reset/core.c
+++ b/drivers/reset/core.c
@@ -13,6 +13,7 @@
#include <linux/err.h>
#include <linux/export.h>
#include <linux/kernel.h>
+#include <linux/kref.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/reset.h>
@@ -40,7 +41,7 @@ struct reset_control {
struct reset_controller_dev *rcdev;
struct list_head list;
unsigned int id;
- unsigned int refcnt;
+ struct kref refcnt;
bool shared;
atomic_t deassert_count;
atomic_t triggered_count;
@@ -288,7 +289,7 @@ static struct reset_control *__reset_control_get_internal(
if (WARN_ON(!rstc->shared || !shared))
return ERR_PTR(-EBUSY);
- rstc->refcnt++;
+ kref_get(&rstc->refcnt);
return rstc;
}
}
@@ -302,18 +303,18 @@ static struct reset_control *__reset_control_get_internal(
rstc->rcdev = rcdev;
list_add(&rstc->list, &rcdev->reset_control_head);
rstc->id = index;
- rstc->refcnt = 1;
+ kref_init(&rstc->refcnt);
rstc->shared = shared;
return rstc;
}
-static void __reset_control_put_internal(struct reset_control *rstc)
+static void __reset_control_release(struct kref *kref)
{
- lockdep_assert_held(&reset_list_mutex);
+ struct reset_control *rstc = container_of(kref, struct reset_control,
+ refcnt);
- if (--rstc->refcnt)
- return;
+ lockdep_assert_held(&reset_list_mutex);
module_put(rstc->rcdev->owner);
@@ -321,6 +322,13 @@ static void __reset_control_put_internal(struct reset_control *rstc)
kfree(rstc);
}
+static void __reset_control_put_internal(struct reset_control *rstc)
+{
+ lockdep_assert_held(&reset_list_mutex);
+
+ kref_put(&rstc->refcnt, __reset_control_release);
+}
+
struct reset_control *__of_reset_control_get(struct device_node *node,
const char *id, int index, bool shared,
bool optional)
@@ -400,7 +408,6 @@ EXPORT_SYMBOL_GPL(__reset_control_get);
* reset_control_put - free the reset controller
* @rstc: reset controller
*/
-
void reset_control_put(struct reset_control *rstc)
{
if (IS_ERR_OR_NULL(rstc))
diff --git a/drivers/reset/reset-gemini.c b/drivers/reset/reset-gemini.c
new file mode 100644
index 0000000..a247899
--- /dev/null
+++ b/drivers/reset/reset-gemini.c
@@ -0,0 +1,110 @@
+/*
+ * Cortina Gemini Reset controller driver
+ * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reset-controller.h>
+#include <dt-bindings/reset/cortina,gemini-reset.h>
+
+/**
+ * struct gemini_reset - gemini reset controller
+ * @map: regmap to access the containing system controller
+ * @rcdev: reset controller device
+ */
+struct gemini_reset {
+ struct regmap *map;
+ struct reset_controller_dev rcdev;
+};
+
+#define GEMINI_GLOBAL_SOFT_RESET 0x0c
+
+#define to_gemini_reset(p) \
+ container_of((p), struct gemini_reset, rcdev)
+
+/*
+ * This is a self-deasserting reset controller.
+ */
+static int gemini_reset(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct gemini_reset *gr = to_gemini_reset(rcdev);
+
+ /* Manual says to always set BIT 30 (CPU1) to 1 */
+ return regmap_write(gr->map,
+ GEMINI_GLOBAL_SOFT_RESET,
+ BIT(GEMINI_RESET_CPU1) | BIT(id));
+}
+
+static int gemini_reset_status(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct gemini_reset *gr = to_gemini_reset(rcdev);
+ u32 val;
+ int ret;
+
+ ret = regmap_read(gr->map, GEMINI_GLOBAL_SOFT_RESET, &val);
+ if (ret)
+ return ret;
+
+ return !!(val & BIT(id));
+}
+
+static const struct reset_control_ops gemini_reset_ops = {
+ .reset = gemini_reset,
+ .status = gemini_reset_status,
+};
+
+static int gemini_reset_probe(struct platform_device *pdev)
+{
+ struct gemini_reset *gr;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ int ret;
+
+ gr = devm_kzalloc(dev, sizeof(*gr), GFP_KERNEL);
+ if (!gr)
+ return -ENOMEM;
+
+ gr->map = syscon_node_to_regmap(np);
+ if (IS_ERR(gr->map)) {
+ ret = PTR_ERR(gr->map);
+ dev_err(dev, "unable to get regmap (%d)", ret);
+ return ret;
+ }
+ gr->rcdev.owner = THIS_MODULE;
+ gr->rcdev.nr_resets = 32;
+ gr->rcdev.ops = &gemini_reset_ops;
+ gr->rcdev.of_node = pdev->dev.of_node;
+
+ ret = devm_reset_controller_register(&pdev->dev, &gr->rcdev);
+ if (ret)
+ return ret;
+
+ dev_info(dev, "registered Gemini reset controller\n");
+ return 0;
+}
+
+static const struct of_device_id gemini_reset_dt_ids[] = {
+ { .compatible = "cortina,gemini-syscon", },
+ { /* sentinel */ },
+};
+
+static struct platform_driver gemini_reset_driver = {
+ .probe = gemini_reset_probe,
+ .driver = {
+ .name = "gemini-reset",
+ .of_match_table = gemini_reset_dt_ids,
+ .suppress_bind_attrs = true,
+ },
+};
+builtin_platform_driver(gemini_reset_driver);
diff --git a/drivers/reset/reset-ti-sci.c b/drivers/reset/reset-ti-sci.c
new file mode 100644
index 0000000..bf68729
--- /dev/null
+++ b/drivers/reset/reset-ti-sci.c
@@ -0,0 +1,269 @@
+/*
+ * Texas Instrument's System Control Interface (TI-SCI) reset driver
+ *
+ * Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com/
+ * Andrew F. Davis <afd@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/idr.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reset-controller.h>
+#include <linux/soc/ti/ti_sci_protocol.h>
+
+/**
+ * struct ti_sci_reset_control - reset control structure
+ * @dev_id: SoC-specific device identifier
+ * @reset_mask: reset mask to use for toggling reset
+ * @lock: synchronize reset_mask read-modify-writes
+ */
+struct ti_sci_reset_control {
+ u32 dev_id;
+ u32 reset_mask;
+ struct mutex lock;
+};
+
+/**
+ * struct ti_sci_reset_data - reset controller information structure
+ * @rcdev: reset controller entity
+ * @dev: reset controller device pointer
+ * @sci: TI SCI handle used for communication with system controller
+ * @idr: idr structure for mapping ids to reset control structures
+ */
+struct ti_sci_reset_data {
+ struct reset_controller_dev rcdev;
+ struct device *dev;
+ const struct ti_sci_handle *sci;
+ struct idr idr;
+};
+
+#define to_ti_sci_reset_data(p) \
+ container_of((p), struct ti_sci_reset_data, rcdev)
+
+/**
+ * ti_sci_reset_set() - program a device's reset
+ * @rcdev: reset controller entity
+ * @id: ID of the reset to toggle
+ * @assert: boolean flag to indicate assert or deassert
+ *
+ * This is a common internal function used to assert or deassert a device's
+ * reset using the TI SCI protocol. The device's reset is asserted if the
+ * @assert argument is true, or deasserted if @assert argument is false.
+ * The mechanism itself is a read-modify-write procedure, the current device
+ * reset register is read using a TI SCI device operation, the new value is
+ * set or un-set using the reset's mask, and the new reset value written by
+ * using another TI SCI device operation.
+ *
+ * Return: 0 for successful request, else a corresponding error value
+ */
+static int ti_sci_reset_set(struct reset_controller_dev *rcdev,
+ unsigned long id, bool assert)
+{
+ struct ti_sci_reset_data *data = to_ti_sci_reset_data(rcdev);
+ const struct ti_sci_handle *sci = data->sci;
+ const struct ti_sci_dev_ops *dev_ops = &sci->ops.dev_ops;
+ struct ti_sci_reset_control *control;
+ u32 reset_state;
+ int ret;
+
+ control = idr_find(&data->idr, id);
+ if (!control)
+ return -EINVAL;
+
+ mutex_lock(&control->lock);
+
+ ret = dev_ops->get_device_resets(sci, control->dev_id, &reset_state);
+ if (ret)
+ goto out;
+
+ if (assert)
+ reset_state |= control->reset_mask;
+ else
+ reset_state &= ~control->reset_mask;
+
+ ret = dev_ops->set_device_resets(sci, control->dev_id, reset_state);
+out:
+ mutex_unlock(&control->lock);
+
+ return ret;
+}
+
+/**
+ * ti_sci_reset_assert() - assert device reset
+ * @rcdev: reset controller entity
+ * @id: ID of the reset to be asserted
+ *
+ * This function implements the reset driver op to assert a device's reset
+ * using the TI SCI protocol. This invokes the function ti_sci_reset_set()
+ * with the corresponding parameters as passed in, but with the @assert
+ * argument set to true for asserting the reset.
+ *
+ * Return: 0 for successful request, else a corresponding error value
+ */
+static int ti_sci_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return ti_sci_reset_set(rcdev, id, true);
+}
+
+/**
+ * ti_sci_reset_deassert() - deassert device reset
+ * @rcdev: reset controller entity
+ * @id: ID of the reset to be deasserted
+ *
+ * This function implements the reset driver op to deassert a device's reset
+ * using the TI SCI protocol. This invokes the function ti_sci_reset_set()
+ * with the corresponding parameters as passed in, but with the @assert
+ * argument set to false for deasserting the reset.
+ *
+ * Return: 0 for successful request, else a corresponding error value
+ */
+static int ti_sci_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return ti_sci_reset_set(rcdev, id, false);
+}
+
+/**
+ * ti_sci_reset_status() - check device reset status
+ * @rcdev: reset controller entity
+ * @id: ID of reset to be checked
+ *
+ * This function implements the reset driver op to return the status of a
+ * device's reset using the TI SCI protocol. The reset register value is read
+ * by invoking the TI SCI device operation .get_device_resets(), and the
+ * status of the specific reset is extracted and returned using this reset's
+ * reset mask.
+ *
+ * Return: 0 if reset is deasserted, or a non-zero value if reset is asserted
+ */
+static int ti_sci_reset_status(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct ti_sci_reset_data *data = to_ti_sci_reset_data(rcdev);
+ const struct ti_sci_handle *sci = data->sci;
+ const struct ti_sci_dev_ops *dev_ops = &sci->ops.dev_ops;
+ struct ti_sci_reset_control *control;
+ u32 reset_state;
+ int ret;
+
+ control = idr_find(&data->idr, id);
+ if (!control)
+ return -EINVAL;
+
+ ret = dev_ops->get_device_resets(sci, control->dev_id, &reset_state);
+ if (ret)
+ return ret;
+
+ return reset_state & control->reset_mask;
+}
+
+static const struct reset_control_ops ti_sci_reset_ops = {
+ .assert = ti_sci_reset_assert,
+ .deassert = ti_sci_reset_deassert,
+ .status = ti_sci_reset_status,
+};
+
+/**
+ * ti_sci_reset_of_xlate() - translate a set of OF arguments to a reset ID
+ * @rcdev: reset controller entity
+ * @reset_spec: OF reset argument specifier
+ *
+ * This function performs the translation of the reset argument specifier
+ * values defined in a reset consumer device node. The function allocates a
+ * reset control structure for that device reset, and will be used by the
+ * driver for performing any reset functions on that reset. An idr structure
+ * is allocated and used to map to the reset control structure. This idr
+ * is used by the driver to do reset lookups.
+ *
+ * Return: 0 for successful request, else a corresponding error value
+ */
+static int ti_sci_reset_of_xlate(struct reset_controller_dev *rcdev,
+ const struct of_phandle_args *reset_spec)
+{
+ struct ti_sci_reset_data *data = to_ti_sci_reset_data(rcdev);
+ struct ti_sci_reset_control *control;
+
+ if (WARN_ON(reset_spec->args_count != rcdev->of_reset_n_cells))
+ return -EINVAL;
+
+ control = devm_kzalloc(data->dev, sizeof(*control), GFP_KERNEL);
+ if (!control)
+ return -ENOMEM;
+
+ control->dev_id = reset_spec->args[0];
+ control->reset_mask = reset_spec->args[1];
+ mutex_init(&control->lock);
+
+ return idr_alloc(&data->idr, control, 0, 0, GFP_KERNEL);
+}
+
+static const struct of_device_id ti_sci_reset_of_match[] = {
+ { .compatible = "ti,sci-reset", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, ti_sci_reset_of_match);
+
+static int ti_sci_reset_probe(struct platform_device *pdev)
+{
+ struct ti_sci_reset_data *data;
+
+ if (!pdev->dev.of_node)
+ return -ENODEV;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->sci = devm_ti_sci_get_handle(&pdev->dev);
+ if (IS_ERR(data->sci))
+ return PTR_ERR(data->sci);
+
+ data->rcdev.ops = &ti_sci_reset_ops;
+ data->rcdev.owner = THIS_MODULE;
+ data->rcdev.of_node = pdev->dev.of_node;
+ data->rcdev.of_reset_n_cells = 2;
+ data->rcdev.of_xlate = ti_sci_reset_of_xlate;
+ data->dev = &pdev->dev;
+ idr_init(&data->idr);
+
+ platform_set_drvdata(pdev, data);
+
+ return reset_controller_register(&data->rcdev);
+}
+
+static int ti_sci_reset_remove(struct platform_device *pdev)
+{
+ struct ti_sci_reset_data *data = platform_get_drvdata(pdev);
+
+ reset_controller_unregister(&data->rcdev);
+
+ idr_destroy(&data->idr);
+
+ return 0;
+}
+
+static struct platform_driver ti_sci_reset_driver = {
+ .probe = ti_sci_reset_probe,
+ .remove = ti_sci_reset_remove,
+ .driver = {
+ .name = "ti-sci-reset",
+ .of_match_table = ti_sci_reset_of_match,
+ },
+};
+module_platform_driver(ti_sci_reset_driver);
+
+MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
+MODULE_DESCRIPTION("TI System Control Interface (TI SCI) Reset driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/reset/sti/reset-syscfg.c b/drivers/reset/sti/reset-syscfg.c
index 9bd57a5..7e0f2aa 100644
--- a/drivers/reset/sti/reset-syscfg.c
+++ b/drivers/reset/sti/reset-syscfg.c
@@ -145,16 +145,14 @@ static int syscfg_reset_controller_register(struct device *dev,
const struct syscfg_reset_controller_data *data)
{
struct syscfg_reset_controller *rc;
- size_t size;
int i, err;
rc = devm_kzalloc(dev, sizeof(*rc), GFP_KERNEL);
if (!rc)
return -ENOMEM;
- size = sizeof(struct syscfg_reset_channel) * data->nr_channels;
-
- rc->channels = devm_kzalloc(dev, size, GFP_KERNEL);
+ rc->channels = devm_kcalloc(dev, data->nr_channels,
+ sizeof(*rc->channels), GFP_KERNEL);
if (!rc->channels)
return -ENOMEM;
diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
index a63eb0f..07fc0ac 100644
--- a/drivers/soc/Kconfig
+++ b/drivers/soc/Kconfig
@@ -7,6 +7,7 @@ source "drivers/soc/fsl/Kconfig"
source "drivers/soc/imx/Kconfig"
source "drivers/soc/mediatek/Kconfig"
source "drivers/soc/qcom/Kconfig"
+source "drivers/soc/renesas/Kconfig"
source "drivers/soc/rockchip/Kconfig"
source "drivers/soc/samsung/Kconfig"
source "drivers/soc/sunxi/Kconfig"
diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
index a3b27a3..9241125 100644
--- a/drivers/soc/Makefile
+++ b/drivers/soc/Makefile
@@ -11,7 +11,7 @@ obj-y += fsl/
obj-$(CONFIG_ARCH_MXC) += imx/
obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/
obj-$(CONFIG_ARCH_QCOM) += qcom/
-obj-$(CONFIG_ARCH_RENESAS) += renesas/
+obj-y += renesas/
obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
obj-$(CONFIG_SOC_SAMSUNG) += samsung/
obj-$(CONFIG_ARCH_SUNXI) += sunxi/
diff --git a/drivers/soc/bcm/Kconfig b/drivers/soc/bcm/Kconfig
index a39b0d5..49f1e2a 100644
--- a/drivers/soc/bcm/Kconfig
+++ b/drivers/soc/bcm/Kconfig
@@ -11,7 +11,7 @@ config RASPBERRYPI_POWER
config SOC_BRCMSTB
bool "Broadcom STB SoC drivers"
- depends on ARM
+ depends on ARM || ARM64 || BMIPS_GENERIC || COMPILE_TEST
select SOC_BUS
help
Enables drivers for the Broadcom Set-Top Box (STB) series of chips.
diff --git a/drivers/soc/imx/Makefile b/drivers/soc/imx/Makefile
index 5b6e396..aab41a5c 100644
--- a/drivers/soc/imx/Makefile
+++ b/drivers/soc/imx/Makefile
@@ -1,2 +1,2 @@
-obj-y += gpc.o
+obj-$(CONFIG_HAVE_IMX_GPC) += gpc.o
obj-$(CONFIG_IMX7_PM_DOMAINS) += gpcv2.o
diff --git a/drivers/soc/mediatek/mtk-pmic-wrap.c b/drivers/soc/mediatek/mtk-pmic-wrap.c
index a5f1093..c80a04e 100644
--- a/drivers/soc/mediatek/mtk-pmic-wrap.c
+++ b/drivers/soc/mediatek/mtk-pmic-wrap.c
@@ -1117,6 +1117,11 @@ static int pwrap_probe(struct platform_device *pdev)
const struct of_device_id *of_slave_id = NULL;
struct resource *res;
+ if (!of_id) {
+ dev_err(&pdev->dev, "Error: No device match found\n");
+ return -ENODEV;
+ }
+
if (pdev->dev.of_node->child)
of_slave_id = of_match_node(of_slave_match_tbl,
pdev->dev.of_node->child);
@@ -1200,7 +1205,8 @@ static int pwrap_probe(struct platform_device *pdev)
if (!(pwrap_readl(wrp, PWRAP_WACS2_RDATA) & PWRAP_STATE_INIT_DONE0)) {
dev_dbg(wrp->dev, "initialization isn't finished\n");
- return -ENODEV;
+ ret = -ENODEV;
+ goto err_out2;
}
/* Initialize watchdog, may not be done by the bootloader */
@@ -1220,8 +1226,10 @@ static int pwrap_probe(struct platform_device *pdev)
goto err_out2;
wrp->regmap = devm_regmap_init(wrp->dev, NULL, wrp, &pwrap_regmap_config);
- if (IS_ERR(wrp->regmap))
- return PTR_ERR(wrp->regmap);
+ if (IS_ERR(wrp->regmap)) {
+ ret = PTR_ERR(wrp->regmap);
+ goto err_out2;
+ }
ret = of_platform_populate(np, NULL, NULL, wrp->dev);
if (ret) {
diff --git a/drivers/soc/mediatek/mtk-scpsys.c b/drivers/soc/mediatek/mtk-scpsys.c
index beb7916..ceb2cc4 100644
--- a/drivers/soc/mediatek/mtk-scpsys.c
+++ b/drivers/soc/mediatek/mtk-scpsys.c
@@ -21,6 +21,7 @@
#include <linux/soc/mediatek/infracfg.h>
#include <dt-bindings/power/mt2701-power.h>
+#include <dt-bindings/power/mt6797-power.h>
#include <dt-bindings/power/mt8173-power.h>
#define SPM_VDE_PWR_CON 0x0210
@@ -71,6 +72,7 @@ enum clk_id {
CLK_VENC,
CLK_VENC_LT,
CLK_ETHIF,
+ CLK_VDEC,
CLK_MAX,
};
@@ -81,6 +83,7 @@ static const char * const clk_names[] = {
"venc",
"venc_lt",
"ethif",
+ "vdec",
NULL,
};
@@ -107,21 +110,28 @@ struct scp_domain {
struct regulator *supply;
};
+struct scp_ctrl_reg {
+ int pwr_sta_offs;
+ int pwr_sta2nd_offs;
+};
+
struct scp {
struct scp_domain *domains;
struct genpd_onecell_data pd_data;
struct device *dev;
void __iomem *base;
struct regmap *infracfg;
+ struct scp_ctrl_reg ctrl_reg;
};
static int scpsys_domain_is_on(struct scp_domain *scpd)
{
struct scp *scp = scpd->scp;
- u32 status = readl(scp->base + SPM_PWR_STATUS) & scpd->data->sta_mask;
- u32 status2 = readl(scp->base + SPM_PWR_STATUS_2ND) &
- scpd->data->sta_mask;
+ u32 status = readl(scp->base + scp->ctrl_reg.pwr_sta_offs) &
+ scpd->data->sta_mask;
+ u32 status2 = readl(scp->base + scp->ctrl_reg.pwr_sta2nd_offs) &
+ scpd->data->sta_mask;
/*
* A domain is on when both status bits are set. If only one is set
@@ -346,7 +356,8 @@ static void init_clks(struct platform_device *pdev, struct clk **clk)
}
static struct scp *init_scp(struct platform_device *pdev,
- const struct scp_domain_data *scp_domain_data, int num)
+ const struct scp_domain_data *scp_domain_data, int num,
+ struct scp_ctrl_reg *scp_ctrl_reg)
{
struct genpd_onecell_data *pd_data;
struct resource *res;
@@ -358,6 +369,9 @@ static struct scp *init_scp(struct platform_device *pdev,
if (!scp)
return ERR_PTR(-ENOMEM);
+ scp->ctrl_reg.pwr_sta_offs = scp_ctrl_reg->pwr_sta_offs;
+ scp->ctrl_reg.pwr_sta2nd_offs = scp_ctrl_reg->pwr_sta2nd_offs;
+
scp->dev = &pdev->dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -556,8 +570,13 @@ static const struct scp_domain_data scp_domain_data_mt2701[] = {
static int __init scpsys_probe_mt2701(struct platform_device *pdev)
{
struct scp *scp;
+ struct scp_ctrl_reg scp_reg;
- scp = init_scp(pdev, scp_domain_data_mt2701, NUM_DOMAINS_MT2701);
+ scp_reg.pwr_sta_offs = SPM_PWR_STATUS;
+ scp_reg.pwr_sta2nd_offs = SPM_PWR_STATUS_2ND;
+
+ scp = init_scp(pdev, scp_domain_data_mt2701, NUM_DOMAINS_MT2701,
+ &scp_reg);
if (IS_ERR(scp))
return PTR_ERR(scp);
@@ -567,6 +586,116 @@ static int __init scpsys_probe_mt2701(struct platform_device *pdev)
}
/*
+ * MT6797 power domain support
+ */
+
+static const struct scp_domain_data scp_domain_data_mt6797[] = {
+ [MT6797_POWER_DOMAIN_VDEC] = {
+ .name = "vdec",
+ .sta_mask = BIT(7),
+ .ctl_offs = 0x300,
+ .sram_pdn_bits = GENMASK(8, 8),
+ .sram_pdn_ack_bits = GENMASK(12, 12),
+ .clk_id = {CLK_VDEC},
+ },
+ [MT6797_POWER_DOMAIN_VENC] = {
+ .name = "venc",
+ .sta_mask = BIT(21),
+ .ctl_offs = 0x304,
+ .sram_pdn_bits = GENMASK(11, 8),
+ .sram_pdn_ack_bits = GENMASK(15, 12),
+ .clk_id = {CLK_NONE},
+ },
+ [MT6797_POWER_DOMAIN_ISP] = {
+ .name = "isp",
+ .sta_mask = BIT(5),
+ .ctl_offs = 0x308,
+ .sram_pdn_bits = GENMASK(9, 8),
+ .sram_pdn_ack_bits = GENMASK(13, 12),
+ .clk_id = {CLK_NONE},
+ },
+ [MT6797_POWER_DOMAIN_MM] = {
+ .name = "mm",
+ .sta_mask = BIT(3),
+ .ctl_offs = 0x30C,
+ .sram_pdn_bits = GENMASK(8, 8),
+ .sram_pdn_ack_bits = GENMASK(12, 12),
+ .clk_id = {CLK_MM},
+ .bus_prot_mask = (BIT(1) | BIT(2)),
+ },
+ [MT6797_POWER_DOMAIN_AUDIO] = {
+ .name = "audio",
+ .sta_mask = BIT(24),
+ .ctl_offs = 0x314,
+ .sram_pdn_bits = GENMASK(11, 8),
+ .sram_pdn_ack_bits = GENMASK(15, 12),
+ .clk_id = {CLK_NONE},
+ },
+ [MT6797_POWER_DOMAIN_MFG_ASYNC] = {
+ .name = "mfg_async",
+ .sta_mask = BIT(13),
+ .ctl_offs = 0x334,
+ .sram_pdn_bits = 0,
+ .sram_pdn_ack_bits = 0,
+ .clk_id = {CLK_MFG},
+ },
+ [MT6797_POWER_DOMAIN_MJC] = {
+ .name = "mjc",
+ .sta_mask = BIT(20),
+ .ctl_offs = 0x310,
+ .sram_pdn_bits = GENMASK(8, 8),
+ .sram_pdn_ack_bits = GENMASK(12, 12),
+ .clk_id = {CLK_NONE},
+ },
+};
+
+#define NUM_DOMAINS_MT6797 ARRAY_SIZE(scp_domain_data_mt6797)
+#define SPM_PWR_STATUS_MT6797 0x0180
+#define SPM_PWR_STATUS_2ND_MT6797 0x0184
+
+static int __init scpsys_probe_mt6797(struct platform_device *pdev)
+{
+ struct scp *scp;
+ struct genpd_onecell_data *pd_data;
+ int ret;
+ struct scp_ctrl_reg scp_reg;
+
+ scp_reg.pwr_sta_offs = SPM_PWR_STATUS_MT6797;
+ scp_reg.pwr_sta2nd_offs = SPM_PWR_STATUS_2ND_MT6797;
+
+ scp = init_scp(pdev, scp_domain_data_mt6797, NUM_DOMAINS_MT6797,
+ &scp_reg);
+ if (IS_ERR(scp))
+ return PTR_ERR(scp);
+
+ mtk_register_power_domains(pdev, scp, NUM_DOMAINS_MT6797);
+
+ pd_data = &scp->pd_data;
+
+ ret = pm_genpd_add_subdomain(pd_data->domains[MT6797_POWER_DOMAIN_MM],
+ pd_data->domains[MT6797_POWER_DOMAIN_VDEC]);
+ if (ret && IS_ENABLED(CONFIG_PM))
+ dev_err(&pdev->dev, "Failed to add subdomain: %d\n", ret);
+
+ ret = pm_genpd_add_subdomain(pd_data->domains[MT6797_POWER_DOMAIN_MM],
+ pd_data->domains[MT6797_POWER_DOMAIN_ISP]);
+ if (ret && IS_ENABLED(CONFIG_PM))
+ dev_err(&pdev->dev, "Failed to add subdomain: %d\n", ret);
+
+ ret = pm_genpd_add_subdomain(pd_data->domains[MT6797_POWER_DOMAIN_MM],
+ pd_data->domains[MT6797_POWER_DOMAIN_VENC]);
+ if (ret && IS_ENABLED(CONFIG_PM))
+ dev_err(&pdev->dev, "Failed to add subdomain: %d\n", ret);
+
+ ret = pm_genpd_add_subdomain(pd_data->domains[MT6797_POWER_DOMAIN_MM],
+ pd_data->domains[MT6797_POWER_DOMAIN_MJC]);
+ if (ret && IS_ENABLED(CONFIG_PM))
+ dev_err(&pdev->dev, "Failed to add subdomain: %d\n", ret);
+
+ return 0;
+}
+
+/*
* MT8173 power domain support
*/
@@ -667,8 +796,13 @@ static int __init scpsys_probe_mt8173(struct platform_device *pdev)
struct scp *scp;
struct genpd_onecell_data *pd_data;
int ret;
+ struct scp_ctrl_reg scp_reg;
- scp = init_scp(pdev, scp_domain_data_mt8173, NUM_DOMAINS_MT8173);
+ scp_reg.pwr_sta_offs = SPM_PWR_STATUS;
+ scp_reg.pwr_sta2nd_offs = SPM_PWR_STATUS_2ND;
+
+ scp = init_scp(pdev, scp_domain_data_mt8173, NUM_DOMAINS_MT8173,
+ &scp_reg);
if (IS_ERR(scp))
return PTR_ERR(scp);
@@ -698,6 +832,9 @@ static const struct of_device_id of_scpsys_match_tbl[] = {
.compatible = "mediatek,mt2701-scpsys",
.data = scpsys_probe_mt2701,
}, {
+ .compatible = "mediatek,mt6797-scpsys",
+ .data = scpsys_probe_mt6797,
+ }, {
.compatible = "mediatek,mt8173-scpsys",
.data = scpsys_probe_mt8173,
}, {
diff --git a/drivers/soc/qcom/smsm.c b/drivers/soc/qcom/smsm.c
index d0337b2..dc540ea9 100644
--- a/drivers/soc/qcom/smsm.c
+++ b/drivers/soc/qcom/smsm.c
@@ -439,14 +439,15 @@ static int smsm_get_size_info(struct qcom_smsm *smsm)
} *info;
info = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_SMSM_SIZE_INFO, &size);
- if (PTR_ERR(info) == -ENOENT || size != sizeof(*info)) {
+ if (IS_ERR(info) && PTR_ERR(info) != -ENOENT) {
+ if (PTR_ERR(info) != -EPROBE_DEFER)
+ dev_err(smsm->dev, "unable to retrieve smsm size info\n");
+ return PTR_ERR(info);
+ } else if (IS_ERR(info) || size != sizeof(*info)) {
dev_warn(smsm->dev, "no smsm size info, using defaults\n");
smsm->num_entries = SMSM_DEFAULT_NUM_ENTRIES;
smsm->num_hosts = SMSM_DEFAULT_NUM_HOSTS;
return 0;
- } else if (IS_ERR(info)) {
- dev_err(smsm->dev, "unable to retrieve smsm size info\n");
- return PTR_ERR(info);
}
smsm->num_entries = info->num_entries;
diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig
new file mode 100644
index 0000000..87a4be4
--- /dev/null
+++ b/drivers/soc/renesas/Kconfig
@@ -0,0 +1,63 @@
+config SOC_RENESAS
+ bool "Renesas SoC driver support" if COMPILE_TEST && !ARCH_RENESAS
+ default y if ARCH_RENESAS
+ select SOC_BUS
+ select RST_RCAR if ARCH_RCAR_GEN1 || ARCH_RCAR_GEN2 || \
+ ARCH_R8A7795 || ARCH_R8A7796
+ select SYSC_R8A7743 if ARCH_R8A7743
+ select SYSC_R8A7745 if ARCH_R8A7745
+ select SYSC_R8A7779 if ARCH_R8A7779
+ select SYSC_R8A7790 if ARCH_R8A7790
+ select SYSC_R8A7791 if ARCH_R8A7791 || ARCH_R8A7793
+ select SYSC_R8A7792 if ARCH_R8A7792
+ select SYSC_R8A7794 if ARCH_R8A7794
+ select SYSC_R8A7795 if ARCH_R8A7795
+ select SYSC_R8A7796 if ARCH_R8A7796
+
+if SOC_RENESAS
+
+# SoC
+config SYSC_R8A7743
+ bool "RZ/G1M System Controller support" if COMPILE_TEST
+ select SYSC_RCAR
+
+config SYSC_R8A7745
+ bool "RZ/G1E System Controller support" if COMPILE_TEST
+ select SYSC_RCAR
+
+config SYSC_R8A7779
+ bool "R-Car H1 System Controller support" if COMPILE_TEST
+ select SYSC_RCAR
+
+config SYSC_R8A7790
+ bool "R-Car H2 System Controller support" if COMPILE_TEST
+ select SYSC_RCAR
+
+config SYSC_R8A7791
+ bool "R-Car M2-W/N System Controller support" if COMPILE_TEST
+ select SYSC_RCAR
+
+config SYSC_R8A7792
+ bool "R-Car V2H System Controller support" if COMPILE_TEST
+ select SYSC_RCAR
+
+config SYSC_R8A7794
+ bool "R-Car E2 System Controller support" if COMPILE_TEST
+ select SYSC_RCAR
+
+config SYSC_R8A7795
+ bool "R-Car H3 System Controller support" if COMPILE_TEST
+ select SYSC_RCAR
+
+config SYSC_R8A7796
+ bool "R-Car M3-W System Controller support" if COMPILE_TEST
+ select SYSC_RCAR
+
+# Family
+config RST_RCAR
+ bool "R-Car Reset Controller support" if COMPILE_TEST
+
+config SYSC_RCAR
+ bool "R-Car System Controller support" if COMPILE_TEST
+
+endif # SOC_RENESAS
diff --git a/drivers/soc/renesas/Makefile b/drivers/soc/renesas/Makefile
index d9115cb..1a1a297 100644
--- a/drivers/soc/renesas/Makefile
+++ b/drivers/soc/renesas/Makefile
@@ -1,18 +1,17 @@
-obj-$(CONFIG_SOC_BUS) += renesas-soc.o
+# Generic, must be first because of soc_device_register()
+obj-$(CONFIG_SOC_RENESAS) += renesas-soc.o
-obj-$(CONFIG_ARCH_RCAR_GEN1) += rcar-rst.o
-obj-$(CONFIG_ARCH_RCAR_GEN2) += rcar-rst.o
-obj-$(CONFIG_ARCH_R8A7795) += rcar-rst.o
-obj-$(CONFIG_ARCH_R8A7796) += rcar-rst.o
+# SoC
+obj-$(CONFIG_SYSC_R8A7743) += r8a7743-sysc.o
+obj-$(CONFIG_SYSC_R8A7745) += r8a7745-sysc.o
+obj-$(CONFIG_SYSC_R8A7779) += r8a7779-sysc.o
+obj-$(CONFIG_SYSC_R8A7790) += r8a7790-sysc.o
+obj-$(CONFIG_SYSC_R8A7791) += r8a7791-sysc.o
+obj-$(CONFIG_SYSC_R8A7792) += r8a7792-sysc.o
+obj-$(CONFIG_SYSC_R8A7794) += r8a7794-sysc.o
+obj-$(CONFIG_SYSC_R8A7795) += r8a7795-sysc.o
+obj-$(CONFIG_SYSC_R8A7796) += r8a7796-sysc.o
-obj-$(CONFIG_ARCH_R8A7743) += rcar-sysc.o r8a7743-sysc.o
-obj-$(CONFIG_ARCH_R8A7745) += rcar-sysc.o r8a7745-sysc.o
-obj-$(CONFIG_ARCH_R8A7779) += rcar-sysc.o r8a7779-sysc.o
-obj-$(CONFIG_ARCH_R8A7790) += rcar-sysc.o r8a7790-sysc.o
-obj-$(CONFIG_ARCH_R8A7791) += rcar-sysc.o r8a7791-sysc.o
-obj-$(CONFIG_ARCH_R8A7792) += rcar-sysc.o r8a7792-sysc.o
-# R-Car M2-N is identical to R-Car M2-W w.r.t. power domains.
-obj-$(CONFIG_ARCH_R8A7793) += rcar-sysc.o r8a7791-sysc.o
-obj-$(CONFIG_ARCH_R8A7794) += rcar-sysc.o r8a7794-sysc.o
-obj-$(CONFIG_ARCH_R8A7795) += rcar-sysc.o r8a7795-sysc.o
-obj-$(CONFIG_ARCH_R8A7796) += rcar-sysc.o r8a7796-sysc.o
+# Family
+obj-$(CONFIG_RST_RCAR) += rcar-rst.o
+obj-$(CONFIG_SYSC_RCAR) += rcar-sysc.o
diff --git a/drivers/soc/renesas/rcar-sysc.c b/drivers/soc/renesas/rcar-sysc.c
index 528a137..7c8da3c 100644
--- a/drivers/soc/renesas/rcar-sysc.c
+++ b/drivers/soc/renesas/rcar-sysc.c
@@ -181,17 +181,6 @@ static int rcar_sysc_pd_power_off(struct generic_pm_domain *genpd)
struct rcar_sysc_pd *pd = to_rcar_pd(genpd);
pr_debug("%s: %s\n", __func__, genpd->name);
-
- if (pd->flags & PD_NO_CR) {
- pr_debug("%s: Cannot control %s\n", __func__, genpd->name);
- return -EBUSY;
- }
-
- if (pd->flags & PD_BUSY) {
- pr_debug("%s: %s busy\n", __func__, genpd->name);
- return -EBUSY;
- }
-
return rcar_sysc_power_down(&pd->ch);
}
@@ -200,12 +189,6 @@ static int rcar_sysc_pd_power_on(struct generic_pm_domain *genpd)
struct rcar_sysc_pd *pd = to_rcar_pd(genpd);
pr_debug("%s: %s\n", __func__, genpd->name);
-
- if (pd->flags & PD_NO_CR) {
- pr_debug("%s: Cannot control %s\n", __func__, genpd->name);
- return 0;
- }
-
return rcar_sysc_power_up(&pd->ch);
}
@@ -223,8 +206,7 @@ static void __init rcar_sysc_pd_setup(struct rcar_sysc_pd *pd)
* only be turned off if the CPU is not in use.
*/
pr_debug("PM domain %s contains %s\n", name, "CPU");
- pd->flags |= PD_BUSY;
- gov = &pm_domain_always_on_gov;
+ genpd->flags |= GENPD_FLAG_ALWAYS_ON;
} else if (pd->flags & PD_SCU) {
/*
* This domain contains an SCU and cache-controller, and
@@ -232,19 +214,17 @@ static void __init rcar_sysc_pd_setup(struct rcar_sysc_pd *pd)
* not in use.
*/
pr_debug("PM domain %s contains %s\n", name, "SCU");
- pd->flags |= PD_BUSY;
- gov = &pm_domain_always_on_gov;
+ genpd->flags |= GENPD_FLAG_ALWAYS_ON;
} else if (pd->flags & PD_NO_CR) {
/*
* This domain cannot be turned off.
*/
- pd->flags |= PD_BUSY;
- gov = &pm_domain_always_on_gov;
+ genpd->flags |= GENPD_FLAG_ALWAYS_ON;
}
if (!(pd->flags & (PD_CPU | PD_SCU))) {
/* Enable Clock Domain for I/O devices */
- genpd->flags = GENPD_FLAG_PM_CLK;
+ genpd->flags |= GENPD_FLAG_PM_CLK;
if (has_cpg_mstp) {
genpd->attach_dev = cpg_mstp_attach_dev;
genpd->detach_dev = cpg_mstp_detach_dev;
@@ -275,35 +255,33 @@ finalize:
}
static const struct of_device_id rcar_sysc_matches[] = {
-#ifdef CONFIG_ARCH_R8A7743
+#ifdef CONFIG_SYSC_R8A7743
{ .compatible = "renesas,r8a7743-sysc", .data = &r8a7743_sysc_info },
#endif
-#ifdef CONFIG_ARCH_R8A7745
+#ifdef CONFIG_SYSC_R8A7745
{ .compatible = "renesas,r8a7745-sysc", .data = &r8a7745_sysc_info },
#endif
-#ifdef CONFIG_ARCH_R8A7779
+#ifdef CONFIG_SYSC_R8A7779
{ .compatible = "renesas,r8a7779-sysc", .data = &r8a7779_sysc_info },
#endif
-#ifdef CONFIG_ARCH_R8A7790
+#ifdef CONFIG_SYSC_R8A7790
{ .compatible = "renesas,r8a7790-sysc", .data = &r8a7790_sysc_info },
#endif
-#ifdef CONFIG_ARCH_R8A7791
+#ifdef CONFIG_SYSC_R8A7791
{ .compatible = "renesas,r8a7791-sysc", .data = &r8a7791_sysc_info },
-#endif
-#ifdef CONFIG_ARCH_R8A7792
- { .compatible = "renesas,r8a7792-sysc", .data = &r8a7792_sysc_info },
-#endif
-#ifdef CONFIG_ARCH_R8A7793
/* R-Car M2-N is identical to R-Car M2-W w.r.t. power domains. */
{ .compatible = "renesas,r8a7793-sysc", .data = &r8a7791_sysc_info },
#endif
-#ifdef CONFIG_ARCH_R8A7794
+#ifdef CONFIG_SYSC_R8A7792
+ { .compatible = "renesas,r8a7792-sysc", .data = &r8a7792_sysc_info },
+#endif
+#ifdef CONFIG_SYSC_R8A7794
{ .compatible = "renesas,r8a7794-sysc", .data = &r8a7794_sysc_info },
#endif
-#ifdef CONFIG_ARCH_R8A7795
+#ifdef CONFIG_SYSC_R8A7795
{ .compatible = "renesas,r8a7795-sysc", .data = &r8a7795_sysc_info },
#endif
-#ifdef CONFIG_ARCH_R8A7796
+#ifdef CONFIG_SYSC_R8A7796
{ .compatible = "renesas,r8a7796-sysc", .data = &r8a7796_sysc_info },
#endif
{ /* sentinel */ }
diff --git a/drivers/soc/renesas/rcar-sysc.h b/drivers/soc/renesas/rcar-sysc.h
index 07edb04..1a5beba 100644
--- a/drivers/soc/renesas/rcar-sysc.h
+++ b/drivers/soc/renesas/rcar-sysc.h
@@ -20,8 +20,6 @@
#define PD_SCU BIT(1) /* Area contains SCU and L2 cache */
#define PD_NO_CR BIT(2) /* Area lacks PWR{ON,OFF}CR registers */
-#define PD_BUSY BIT(3) /* Busy, for internal use only */
-
#define PD_CPU_CR PD_CPU /* CPU area has CR (R-Car H1) */
#define PD_CPU_NOCR PD_CPU | PD_NO_CR /* CPU area lacks CR (R-Car Gen2/3) */
#define PD_ALWAYS_ON PD_NO_CR /* Always-on area */
diff --git a/drivers/soc/tegra/Kconfig b/drivers/soc/tegra/Kconfig
index dcf088d..1beb7c3 100644
--- a/drivers/soc/tegra/Kconfig
+++ b/drivers/soc/tegra/Kconfig
@@ -115,3 +115,8 @@ config SOC_TEGRA_PMC
config SOC_TEGRA_PMC_TEGRA186
bool
+
+config SOC_TEGRA_POWERGATE_BPMP
+ def_bool y
+ depends on PM_GENERIC_DOMAINS
+ depends on TEGRA_BPMP
diff --git a/drivers/soc/tegra/Makefile b/drivers/soc/tegra/Makefile
index 4f81dd5..0e52b45 100644
--- a/drivers/soc/tegra/Makefile
+++ b/drivers/soc/tegra/Makefile
@@ -4,3 +4,4 @@ obj-y += common.o
obj-$(CONFIG_SOC_TEGRA_FLOWCTRL) += flowctrl.o
obj-$(CONFIG_SOC_TEGRA_PMC) += pmc.o
obj-$(CONFIG_SOC_TEGRA_PMC_TEGRA186) += pmc-tegra186.o
+obj-$(CONFIG_SOC_TEGRA_POWERGATE_BPMP) += powergate-bpmp.o
diff --git a/drivers/soc/tegra/flowctrl.c b/drivers/soc/tegra/flowctrl.c
index 0e345c0..5433cc7 100644
--- a/drivers/soc/tegra/flowctrl.c
+++ b/drivers/soc/tegra/flowctrl.c
@@ -157,7 +157,7 @@ static int tegra_flowctrl_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
tegra_flowctrl_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(tegra_flowctrl_base))
- return PTR_ERR(base);
+ return PTR_ERR(tegra_flowctrl_base);
iounmap(base);
diff --git a/drivers/soc/tegra/powergate-bpmp.c b/drivers/soc/tegra/powergate-bpmp.c
new file mode 100644
index 0000000..8fc3560
--- /dev/null
+++ b/drivers/soc/tegra/powergate-bpmp.c
@@ -0,0 +1,359 @@
+/*
+ * Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+
+#include <soc/tegra/bpmp.h>
+#include <soc/tegra/bpmp-abi.h>
+
+struct tegra_powergate_info {
+ unsigned int id;
+ char *name;
+};
+
+struct tegra_powergate {
+ struct generic_pm_domain genpd;
+ struct tegra_bpmp *bpmp;
+ unsigned int id;
+};
+
+static inline struct tegra_powergate *
+to_tegra_powergate(struct generic_pm_domain *genpd)
+{
+ return container_of(genpd, struct tegra_powergate, genpd);
+}
+
+static int tegra_bpmp_powergate_set_state(struct tegra_bpmp *bpmp,
+ unsigned int id, u32 state)
+{
+ struct mrq_pg_request request;
+ struct tegra_bpmp_message msg;
+
+ memset(&request, 0, sizeof(request));
+ request.cmd = CMD_PG_SET_STATE;
+ request.id = id;
+ request.set_state.state = state;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.mrq = MRQ_PG;
+ msg.tx.data = &request;
+ msg.tx.size = sizeof(request);
+
+ return tegra_bpmp_transfer(bpmp, &msg);
+}
+
+static int tegra_bpmp_powergate_get_state(struct tegra_bpmp *bpmp,
+ unsigned int id)
+{
+ struct mrq_pg_response response;
+ struct mrq_pg_request request;
+ struct tegra_bpmp_message msg;
+ int err;
+
+ memset(&request, 0, sizeof(request));
+ request.cmd = CMD_PG_GET_STATE;
+ request.id = id;
+
+ memset(&response, 0, sizeof(response));
+
+ memset(&msg, 0, sizeof(msg));
+ msg.mrq = MRQ_PG;
+ msg.tx.data = &request;
+ msg.tx.size = sizeof(request);
+ msg.rx.data = &response;
+ msg.rx.size = sizeof(response);
+
+ err = tegra_bpmp_transfer(bpmp, &msg);
+ if (err < 0)
+ return PG_STATE_OFF;
+
+ return response.get_state.state;
+}
+
+static int tegra_bpmp_powergate_get_max_id(struct tegra_bpmp *bpmp)
+{
+ struct mrq_pg_response response;
+ struct mrq_pg_request request;
+ struct tegra_bpmp_message msg;
+ int err;
+
+ memset(&request, 0, sizeof(request));
+ request.cmd = CMD_PG_GET_MAX_ID;
+
+ memset(&response, 0, sizeof(response));
+
+ memset(&msg, 0, sizeof(msg));
+ msg.mrq = MRQ_PG;
+ msg.tx.data = &request;
+ msg.tx.size = sizeof(request);
+ msg.rx.data = &response;
+ msg.rx.size = sizeof(response);
+
+ err = tegra_bpmp_transfer(bpmp, &msg);
+ if (err < 0)
+ return err;
+
+ return response.get_max_id.max_id;
+}
+
+static char *tegra_bpmp_powergate_get_name(struct tegra_bpmp *bpmp,
+ unsigned int id)
+{
+ struct mrq_pg_response response;
+ struct mrq_pg_request request;
+ struct tegra_bpmp_message msg;
+ int err;
+
+ memset(&request, 0, sizeof(request));
+ request.cmd = CMD_PG_GET_NAME;
+ request.id = id;
+
+ memset(&response, 0, sizeof(response));
+
+ memset(&msg, 0, sizeof(msg));
+ msg.mrq = MRQ_PG;
+ msg.tx.data = &request;
+ msg.tx.size = sizeof(request);
+ msg.rx.data = &response;
+ msg.rx.size = sizeof(response);
+
+ err = tegra_bpmp_transfer(bpmp, &msg);
+ if (err < 0)
+ return NULL;
+
+ return kstrdup(response.get_name.name, GFP_KERNEL);
+}
+
+static inline bool tegra_bpmp_powergate_is_powered(struct tegra_bpmp *bpmp,
+ unsigned int id)
+{
+ return tegra_bpmp_powergate_get_state(bpmp, id) != PG_STATE_OFF;
+}
+
+static int tegra_powergate_power_on(struct generic_pm_domain *domain)
+{
+ struct tegra_powergate *powergate = to_tegra_powergate(domain);
+ struct tegra_bpmp *bpmp = powergate->bpmp;
+
+ return tegra_bpmp_powergate_set_state(bpmp, powergate->id,
+ PG_STATE_ON);
+}
+
+static int tegra_powergate_power_off(struct generic_pm_domain *domain)
+{
+ struct tegra_powergate *powergate = to_tegra_powergate(domain);
+ struct tegra_bpmp *bpmp = powergate->bpmp;
+
+ return tegra_bpmp_powergate_set_state(bpmp, powergate->id,
+ PG_STATE_OFF);
+}
+
+static struct tegra_powergate *
+tegra_powergate_add(struct tegra_bpmp *bpmp,
+ const struct tegra_powergate_info *info)
+{
+ struct tegra_powergate *powergate;
+ bool off;
+ int err;
+
+ off = !tegra_bpmp_powergate_is_powered(bpmp, info->id);
+
+ powergate = devm_kzalloc(bpmp->dev, sizeof(*powergate), GFP_KERNEL);
+ if (!powergate)
+ return ERR_PTR(-ENOMEM);
+
+ powergate->id = info->id;
+ powergate->bpmp = bpmp;
+
+ powergate->genpd.name = kstrdup(info->name, GFP_KERNEL);
+ powergate->genpd.power_on = tegra_powergate_power_on;
+ powergate->genpd.power_off = tegra_powergate_power_off;
+
+ err = pm_genpd_init(&powergate->genpd, NULL, off);
+ if (err < 0) {
+ kfree(powergate->genpd.name);
+ return ERR_PTR(err);
+ }
+
+ return powergate;
+}
+
+static void tegra_powergate_remove(struct tegra_powergate *powergate)
+{
+ struct generic_pm_domain *genpd = &powergate->genpd;
+ struct tegra_bpmp *bpmp = powergate->bpmp;
+ int err;
+
+ err = pm_genpd_remove(genpd);
+ if (err < 0)
+ dev_err(bpmp->dev, "failed to remove power domain %s: %d\n",
+ genpd->name, err);
+
+ kfree(genpd->name);
+}
+
+static int
+tegra_bpmp_probe_powergates(struct tegra_bpmp *bpmp,
+ struct tegra_powergate_info **powergatesp)
+{
+ struct tegra_powergate_info *powergates;
+ unsigned int max_id, id, count = 0;
+ unsigned int num_holes = 0;
+ int err;
+
+ err = tegra_bpmp_powergate_get_max_id(bpmp);
+ if (err < 0)
+ return err;
+
+ max_id = err;
+
+ dev_dbg(bpmp->dev, "maximum powergate ID: %u\n", max_id);
+
+ powergates = kcalloc(max_id + 1, sizeof(*powergates), GFP_KERNEL);
+ if (!powergates)
+ return -ENOMEM;
+
+ for (id = 0; id <= max_id; id++) {
+ struct tegra_powergate_info *info = &powergates[count];
+
+ info->name = tegra_bpmp_powergate_get_name(bpmp, id);
+ if (!info->name || info->name[0] == '\0') {
+ num_holes++;
+ continue;
+ }
+
+ info->id = id;
+ count++;
+ }
+
+ dev_dbg(bpmp->dev, "holes: %u\n", num_holes);
+
+ *powergatesp = powergates;
+
+ return count;
+}
+
+static int tegra_bpmp_add_powergates(struct tegra_bpmp *bpmp,
+ struct tegra_powergate_info *powergates,
+ unsigned int count)
+{
+ struct genpd_onecell_data *genpd = &bpmp->genpd;
+ struct generic_pm_domain **domains;
+ struct tegra_powergate *powergate;
+ unsigned int i;
+ int err;
+
+ domains = kcalloc(count, sizeof(*domains), GFP_KERNEL);
+ if (!domains)
+ return -ENOMEM;
+
+ for (i = 0; i < count; i++) {
+ powergate = tegra_powergate_add(bpmp, &powergates[i]);
+ if (IS_ERR(powergate)) {
+ err = PTR_ERR(powergate);
+ goto remove;
+ }
+
+ dev_dbg(bpmp->dev, "added power domain %s\n",
+ powergate->genpd.name);
+ domains[i] = &powergate->genpd;
+ }
+
+ genpd->num_domains = count;
+ genpd->domains = domains;
+
+ return 0;
+
+remove:
+ while (i--) {
+ powergate = to_tegra_powergate(domains[i]);
+ tegra_powergate_remove(powergate);
+ }
+
+ kfree(genpd->domains);
+ return err;
+}
+
+static void tegra_bpmp_remove_powergates(struct tegra_bpmp *bpmp)
+{
+ struct genpd_onecell_data *genpd = &bpmp->genpd;
+ unsigned int i = genpd->num_domains;
+ struct tegra_powergate *powergate;
+
+ while (i--) {
+ dev_dbg(bpmp->dev, "removing power domain %s\n",
+ genpd->domains[i]->name);
+ powergate = to_tegra_powergate(genpd->domains[i]);
+ tegra_powergate_remove(powergate);
+ }
+}
+
+static struct generic_pm_domain *
+tegra_powergate_xlate(struct of_phandle_args *spec, void *data)
+{
+ struct generic_pm_domain *domain = ERR_PTR(-ENOENT);
+ struct genpd_onecell_data *genpd = data;
+ unsigned int i;
+
+ for (i = 0; i < genpd->num_domains; i++) {
+ struct tegra_powergate *powergate;
+
+ powergate = to_tegra_powergate(genpd->domains[i]);
+ if (powergate->id == spec->args[0]) {
+ domain = &powergate->genpd;
+ break;
+ }
+ }
+
+ return domain;
+}
+
+int tegra_bpmp_init_powergates(struct tegra_bpmp *bpmp)
+{
+ struct device_node *np = bpmp->dev->of_node;
+ struct tegra_powergate_info *powergates;
+ struct device *dev = bpmp->dev;
+ unsigned int count, i;
+ int err;
+
+ err = tegra_bpmp_probe_powergates(bpmp, &powergates);
+ if (err < 0)
+ return err;
+
+ count = err;
+
+ dev_dbg(dev, "%u power domains probed\n", count);
+
+ err = tegra_bpmp_add_powergates(bpmp, powergates, count);
+ if (err < 0)
+ goto free;
+
+ bpmp->genpd.xlate = tegra_powergate_xlate;
+
+ err = of_genpd_add_provider_onecell(np, &bpmp->genpd);
+ if (err < 0) {
+ dev_err(dev, "failed to add power domain provider: %d\n", err);
+ tegra_bpmp_remove_powergates(bpmp);
+ }
+
+free:
+ for (i = 0; i < count; i++)
+ kfree(powergates[i].name);
+
+ kfree(powergates);
+ return err;
+}
OpenPOWER on IntegriCloud