summaryrefslogtreecommitdiffstats
path: root/drivers/soc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/soc')
-rw-r--r--drivers/soc/Kconfig1
-rw-r--r--drivers/soc/Makefile1
-rw-r--r--drivers/soc/bcm/Kconfig18
-rw-r--r--drivers/soc/bcm/Makefile1
-rw-r--r--drivers/soc/bcm/brcmstb/Makefile (renamed from drivers/soc/brcmstb/Makefile)0
-rw-r--r--drivers/soc/bcm/brcmstb/biuctrl.c (renamed from drivers/soc/brcmstb/biuctrl.c)1
-rw-r--r--drivers/soc/bcm/brcmstb/common.c (renamed from drivers/soc/brcmstb/common.c)0
-rw-r--r--drivers/soc/brcmstb/Kconfig10
-rw-r--r--drivers/soc/qcom/smem_state.c12
-rw-r--r--drivers/soc/qcom/smp2p.c7
-rw-r--r--drivers/soc/qcom/smsm.c2
-rw-r--r--drivers/soc/qcom/wcnss_ctrl.c125
-rw-r--r--drivers/soc/renesas/Makefile2
-rw-r--r--drivers/soc/renesas/r8a7792-sysc.c34
-rw-r--r--drivers/soc/renesas/r8a7796-sysc.c48
-rw-r--r--drivers/soc/renesas/rcar-sysc.c45
-rw-r--r--drivers/soc/renesas/rcar-sysc.h2
-rw-r--r--drivers/soc/samsung/Kconfig4
-rw-r--r--drivers/soc/samsung/Makefile1
-rw-r--r--drivers/soc/samsung/pm_domains.c245
-rw-r--r--drivers/soc/tegra/pmc.c149
21 files changed, 618 insertions, 90 deletions
diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
index b9c1bf43ebec..fe42a2fdf351 100644
--- a/drivers/soc/Kconfig
+++ b/drivers/soc/Kconfig
@@ -1,7 +1,6 @@
menu "SOC (System On Chip) specific Drivers"
source "drivers/soc/bcm/Kconfig"
-source "drivers/soc/brcmstb/Kconfig"
source "drivers/soc/fsl/qe/Kconfig"
source "drivers/soc/mediatek/Kconfig"
source "drivers/soc/qcom/Kconfig"
diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
index 02359c95d7f3..50c23d0bd457 100644
--- a/drivers/soc/Makefile
+++ b/drivers/soc/Makefile
@@ -3,7 +3,6 @@
#
obj-y += bcm/
-obj-$(CONFIG_SOC_BRCMSTB) += brcmstb/
obj-$(CONFIG_ARCH_DOVE) += dove/
obj-$(CONFIG_MACH_DOVE) += dove/
obj-y += fsl/
diff --git a/drivers/soc/bcm/Kconfig b/drivers/soc/bcm/Kconfig
index 3066edea184d..a39b0d58ddd0 100644
--- a/drivers/soc/bcm/Kconfig
+++ b/drivers/soc/bcm/Kconfig
@@ -1,9 +1,23 @@
+menu "Broadcom SoC drivers"
+
config RASPBERRYPI_POWER
bool "Raspberry Pi power domain driver"
- depends on ARCH_BCM2835 || COMPILE_TEST
+ depends on ARCH_BCM2835 || (COMPILE_TEST && OF)
depends on RASPBERRYPI_FIRMWARE=y
select PM_GENERIC_DOMAINS if PM
- select PM_GENERIC_DOMAINS_OF if PM
help
This enables support for the RPi power domains which can be enabled
or disabled via the RPi firmware.
+
+config SOC_BRCMSTB
+ bool "Broadcom STB SoC drivers"
+ depends on ARM
+ select SOC_BUS
+ help
+ Enables drivers for the Broadcom Set-Top Box (STB) series of chips.
+ This option alone enables only some support code, while the drivers
+ can be enabled individually within this menu.
+
+ If unsure, say N.
+
+endmenu
diff --git a/drivers/soc/bcm/Makefile b/drivers/soc/bcm/Makefile
index 63aa3eb23087..dc4fced72d21 100644
--- a/drivers/soc/bcm/Makefile
+++ b/drivers/soc/bcm/Makefile
@@ -1 +1,2 @@
obj-$(CONFIG_RASPBERRYPI_POWER) += raspberrypi-power.o
+obj-$(CONFIG_SOC_BRCMSTB) += brcmstb/
diff --git a/drivers/soc/brcmstb/Makefile b/drivers/soc/bcm/brcmstb/Makefile
index 9120b2715d3e..9120b2715d3e 100644
--- a/drivers/soc/brcmstb/Makefile
+++ b/drivers/soc/bcm/brcmstb/Makefile
diff --git a/drivers/soc/brcmstb/biuctrl.c b/drivers/soc/bcm/brcmstb/biuctrl.c
index 9049c076f9a1..3c39415d484f 100644
--- a/drivers/soc/brcmstb/biuctrl.c
+++ b/drivers/soc/bcm/brcmstb/biuctrl.c
@@ -19,6 +19,7 @@
#include <linux/io.h>
#include <linux/of_address.h>
#include <linux/syscore_ops.h>
+#include <linux/soc/brcmstb/brcmstb.h>
#define CPU_CREDIT_REG_OFFSET 0x184
#define CPU_CREDIT_REG_MCPx_WR_PAIRING_EN_MASK 0x70000000
diff --git a/drivers/soc/brcmstb/common.c b/drivers/soc/bcm/brcmstb/common.c
index 94e7335553f4..94e7335553f4 100644
--- a/drivers/soc/brcmstb/common.c
+++ b/drivers/soc/bcm/brcmstb/common.c
diff --git a/drivers/soc/brcmstb/Kconfig b/drivers/soc/brcmstb/Kconfig
deleted file mode 100644
index 7fec3b4c80a1..000000000000
--- a/drivers/soc/brcmstb/Kconfig
+++ /dev/null
@@ -1,10 +0,0 @@
-menuconfig SOC_BRCMSTB
- bool "Broadcom STB SoC drivers"
- depends on ARM
- select SOC_BUS
- help
- Enables drivers for the Broadcom Set-Top Box (STB) series of chips.
- This option alone enables only some support code, while the drivers
- can be enabled individually within this menu.
-
- If unsure, say N.
diff --git a/drivers/soc/qcom/smem_state.c b/drivers/soc/qcom/smem_state.c
index 54261decb369..d5437ca76ed9 100644
--- a/drivers/soc/qcom/smem_state.c
+++ b/drivers/soc/qcom/smem_state.c
@@ -104,26 +104,26 @@ struct qcom_smem_state *qcom_smem_state_get(struct device *dev,
if (con_id) {
index = of_property_match_string(dev->of_node,
- "qcom,state-names",
+ "qcom,smem-state-names",
con_id);
if (index < 0) {
- dev_err(dev, "missing qcom,state-names\n");
+ dev_err(dev, "missing qcom,smem-state-names\n");
return ERR_PTR(index);
}
}
ret = of_parse_phandle_with_args(dev->of_node,
- "qcom,state",
- "#qcom,state-cells",
+ "qcom,smem-states",
+ "#qcom,smem-state-cells",
index,
&args);
if (ret) {
- dev_err(dev, "failed to parse qcom,state property\n");
+ dev_err(dev, "failed to parse qcom,smem-states property\n");
return ERR_PTR(ret);
}
if (args.args_count != 1) {
- dev_err(dev, "invalid #qcom,state-cells\n");
+ dev_err(dev, "invalid #qcom,smem-state-cells\n");
return ERR_PTR(-EINVAL);
}
diff --git a/drivers/soc/qcom/smp2p.c b/drivers/soc/qcom/smp2p.c
index f1eed7f9dd67..f51fb2ea7200 100644
--- a/drivers/soc/qcom/smp2p.c
+++ b/drivers/soc/qcom/smp2p.c
@@ -196,7 +196,7 @@ static irqreturn_t qcom_smp2p_intr(int irq, void *data)
/* Match newly created entries */
for (i = smp2p->valid_entries; i < in->valid_entries; i++) {
list_for_each_entry(entry, &smp2p->inbound, node) {
- memcpy_fromio(buf, in->entries[i].name, sizeof(buf));
+ memcpy(buf, in->entries[i].name, sizeof(buf));
if (!strcmp(buf, entry->name)) {
entry->value = &in->entries[i].value;
break;
@@ -343,12 +343,13 @@ static int qcom_smp2p_outbound_entry(struct qcom_smp2p *smp2p,
/* Allocate an entry from the smem item */
strlcpy(buf, entry->name, SMP2P_MAX_ENTRY_NAME);
- memcpy_toio(out->entries[out->valid_entries].name, buf, SMP2P_MAX_ENTRY_NAME);
- out->valid_entries++;
+ memcpy(out->entries[out->valid_entries].name, buf, SMP2P_MAX_ENTRY_NAME);
/* Make the logical entry reference the physical value */
entry->value = &out->entries[out->valid_entries].value;
+ out->valid_entries++;
+
entry->state = qcom_smem_state_register(node, &smp2p_state_ops, entry);
if (IS_ERR(entry->state)) {
dev_err(smp2p->dev, "failed to register qcom_smem_state\n");
diff --git a/drivers/soc/qcom/smsm.c b/drivers/soc/qcom/smsm.c
index 6b777af1bc19..d0337b2a71c8 100644
--- a/drivers/soc/qcom/smsm.c
+++ b/drivers/soc/qcom/smsm.c
@@ -495,7 +495,7 @@ static int qcom_smsm_probe(struct platform_device *pdev)
if (!smsm->hosts)
return -ENOMEM;
- local_node = of_find_node_with_property(pdev->dev.of_node, "#qcom,state-cells");
+ local_node = of_find_node_with_property(pdev->dev.of_node, "#qcom,smem-state-cells");
if (!local_node) {
dev_err(&pdev->dev, "no state entry\n");
return -EINVAL;
diff --git a/drivers/soc/qcom/wcnss_ctrl.c b/drivers/soc/qcom/wcnss_ctrl.c
index c544f3d2c6ee..520aedd29965 100644
--- a/drivers/soc/qcom/wcnss_ctrl.c
+++ b/drivers/soc/qcom/wcnss_ctrl.c
@@ -1,4 +1,5 @@
/*
+ * Copyright (c) 2016, Linaro Ltd.
* Copyright (c) 2015, Sony Mobile Communications Inc.
*
* This program is free software; you can redistribute it and/or modify
@@ -14,8 +15,16 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/soc/qcom/smd.h>
+#include <linux/io.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/soc/qcom/wcnss_ctrl.h>
#define WCNSS_REQUEST_TIMEOUT (5 * HZ)
+#define WCNSS_CBC_TIMEOUT (10 * HZ)
+
+#define WCNSS_ACK_DONE_BOOTING 1
+#define WCNSS_ACK_COLD_BOOTING 2
#define NV_FRAGMENT_SIZE 3072
#define NVBIN_FILE "wlan/prima/WCNSS_qcom_wlan_nv.bin"
@@ -25,17 +34,19 @@
* @dev: device handle
* @channel: SMD channel handle
* @ack: completion for outstanding requests
+ * @cbc: completion for cbc complete indication
* @ack_status: status of the outstanding request
- * @download_nv_work: worker for uploading nv binary
+ * @probe_work: worker for uploading nv binary
*/
struct wcnss_ctrl {
struct device *dev;
struct qcom_smd_channel *channel;
struct completion ack;
+ struct completion cbc;
int ack_status;
- struct work_struct download_nv_work;
+ struct work_struct probe_work;
};
/* message types */
@@ -48,6 +59,11 @@ enum {
WCNSS_UPLOAD_CAL_RESP,
WCNSS_DOWNLOAD_CAL_REQ,
WCNSS_DOWNLOAD_CAL_RESP,
+ WCNSS_VBAT_LEVEL_IND,
+ WCNSS_BUILD_VERSION_REQ,
+ WCNSS_BUILD_VERSION_RESP,
+ WCNSS_PM_CONFIG_REQ,
+ WCNSS_CBC_COMPLETE_IND,
};
/**
@@ -128,7 +144,7 @@ static int wcnss_ctrl_smd_callback(struct qcom_smd_channel *channel,
version->major, version->minor,
version->version, version->revision);
- schedule_work(&wcnss->download_nv_work);
+ complete(&wcnss->ack);
break;
case WCNSS_DOWNLOAD_NV_RESP:
if (count != sizeof(*nvresp)) {
@@ -141,6 +157,10 @@ static int wcnss_ctrl_smd_callback(struct qcom_smd_channel *channel,
wcnss->ack_status = nvresp->status;
complete(&wcnss->ack);
break;
+ case WCNSS_CBC_COMPLETE_IND:
+ dev_dbg(wcnss->dev, "cold boot complete\n");
+ complete(&wcnss->cbc);
+ break;
default:
dev_info(wcnss->dev, "unknown message type %d\n", hdr->type);
break;
@@ -156,20 +176,32 @@ static int wcnss_ctrl_smd_callback(struct qcom_smd_channel *channel,
static int wcnss_request_version(struct wcnss_ctrl *wcnss)
{
struct wcnss_msg_hdr msg;
+ int ret;
msg.type = WCNSS_VERSION_REQ;
msg.len = sizeof(msg);
+ ret = qcom_smd_send(wcnss->channel, &msg, sizeof(msg));
+ if (ret < 0)
+ return ret;
+
+ ret = wait_for_completion_timeout(&wcnss->ack, WCNSS_CBC_TIMEOUT);
+ if (!ret) {
+ dev_err(wcnss->dev, "timeout waiting for version response\n");
+ return -ETIMEDOUT;
+ }
- return qcom_smd_send(wcnss->channel, &msg, sizeof(msg));
+ return 0;
}
/**
* wcnss_download_nv() - send nv binary to WCNSS
- * @work: work struct to acquire wcnss context
+ * @wcnss: wcnss_ctrl state handle
+ * @expect_cbc: indicator to caller that an cbc event is expected
+ *
+ * Returns 0 on success. Negative errno on failure.
*/
-static void wcnss_download_nv(struct work_struct *work)
+static int wcnss_download_nv(struct wcnss_ctrl *wcnss, bool *expect_cbc)
{
- struct wcnss_ctrl *wcnss = container_of(work, struct wcnss_ctrl, download_nv_work);
struct wcnss_download_nv_req *req;
const struct firmware *fw;
const void *data;
@@ -178,10 +210,10 @@ static void wcnss_download_nv(struct work_struct *work)
req = kzalloc(sizeof(*req) + NV_FRAGMENT_SIZE, GFP_KERNEL);
if (!req)
- return;
+ return -ENOMEM;
ret = request_firmware(&fw, NVBIN_FILE, wcnss->dev);
- if (ret) {
+ if (ret < 0) {
dev_err(wcnss->dev, "Failed to load nv file %s: %d\n",
NVBIN_FILE, ret);
goto free_req;
@@ -207,7 +239,7 @@ static void wcnss_download_nv(struct work_struct *work)
memcpy(req->fragment, data, req->frag_size);
ret = qcom_smd_send(wcnss->channel, req, req->hdr.len);
- if (ret) {
+ if (ret < 0) {
dev_err(wcnss->dev, "failed to send smd packet\n");
goto release_fw;
}
@@ -220,16 +252,58 @@ static void wcnss_download_nv(struct work_struct *work)
} while (left > 0);
ret = wait_for_completion_timeout(&wcnss->ack, WCNSS_REQUEST_TIMEOUT);
- if (!ret)
+ if (!ret) {
dev_err(wcnss->dev, "timeout waiting for nv upload ack\n");
- else if (wcnss->ack_status != 1)
- dev_err(wcnss->dev, "nv upload response failed err: %d\n",
- wcnss->ack_status);
+ ret = -ETIMEDOUT;
+ } else {
+ *expect_cbc = wcnss->ack_status == WCNSS_ACK_COLD_BOOTING;
+ ret = 0;
+ }
release_fw:
release_firmware(fw);
free_req:
kfree(req);
+
+ return ret;
+}
+
+/**
+ * qcom_wcnss_open_channel() - open additional SMD channel to WCNSS
+ * @wcnss: wcnss handle, retrieved from drvdata
+ * @name: SMD channel name
+ * @cb: callback to handle incoming data on the channel
+ */
+struct qcom_smd_channel *qcom_wcnss_open_channel(void *wcnss, const char *name, qcom_smd_cb_t cb)
+{
+ struct wcnss_ctrl *_wcnss = wcnss;
+
+ return qcom_smd_open_channel(_wcnss->channel, name, cb);
+}
+EXPORT_SYMBOL(qcom_wcnss_open_channel);
+
+static void wcnss_async_probe(struct work_struct *work)
+{
+ struct wcnss_ctrl *wcnss = container_of(work, struct wcnss_ctrl, probe_work);
+ bool expect_cbc;
+ int ret;
+
+ ret = wcnss_request_version(wcnss);
+ if (ret < 0)
+ return;
+
+ ret = wcnss_download_nv(wcnss, &expect_cbc);
+ if (ret < 0)
+ return;
+
+ /* Wait for pending cold boot completion if indicated by the nv downloader */
+ if (expect_cbc) {
+ ret = wait_for_completion_timeout(&wcnss->cbc, WCNSS_REQUEST_TIMEOUT);
+ if (!ret)
+ dev_err(wcnss->dev, "expected cold boot completion\n");
+ }
+
+ of_platform_populate(wcnss->dev->of_node, NULL, NULL, wcnss->dev);
}
static int wcnss_ctrl_probe(struct qcom_smd_device *sdev)
@@ -244,25 +318,38 @@ static int wcnss_ctrl_probe(struct qcom_smd_device *sdev)
wcnss->channel = sdev->channel;
init_completion(&wcnss->ack);
- INIT_WORK(&wcnss->download_nv_work, wcnss_download_nv);
+ init_completion(&wcnss->cbc);
+ INIT_WORK(&wcnss->probe_work, wcnss_async_probe);
qcom_smd_set_drvdata(sdev->channel, wcnss);
+ dev_set_drvdata(&sdev->dev, wcnss);
+
+ schedule_work(&wcnss->probe_work);
+
+ return 0;
+}
+
+static void wcnss_ctrl_remove(struct qcom_smd_device *sdev)
+{
+ struct wcnss_ctrl *wcnss = qcom_smd_get_drvdata(sdev->channel);
- return wcnss_request_version(wcnss);
+ cancel_work_sync(&wcnss->probe_work);
+ of_platform_depopulate(&sdev->dev);
}
-static const struct qcom_smd_id wcnss_ctrl_smd_match[] = {
- { .name = "WCNSS_CTRL" },
+static const struct of_device_id wcnss_ctrl_of_match[] = {
+ { .compatible = "qcom,wcnss", },
{}
};
static struct qcom_smd_driver wcnss_ctrl_driver = {
.probe = wcnss_ctrl_probe,
+ .remove = wcnss_ctrl_remove,
.callback = wcnss_ctrl_smd_callback,
- .smd_match_table = wcnss_ctrl_smd_match,
.driver = {
.name = "qcom_wcnss_ctrl",
.owner = THIS_MODULE,
+ .of_match_table = wcnss_ctrl_of_match,
},
};
diff --git a/drivers/soc/renesas/Makefile b/drivers/soc/renesas/Makefile
index 151fcd3f025b..623039c3514c 100644
--- a/drivers/soc/renesas/Makefile
+++ b/drivers/soc/renesas/Makefile
@@ -1,7 +1,9 @@
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
diff --git a/drivers/soc/renesas/r8a7792-sysc.c b/drivers/soc/renesas/r8a7792-sysc.c
new file mode 100644
index 000000000000..ca7467d7b7ec
--- /dev/null
+++ b/drivers/soc/renesas/r8a7792-sysc.c
@@ -0,0 +1,34 @@
+/*
+ * Renesas R-Car V2H (R8A7792) System Controller
+ *
+ * Copyright (C) 2016 Cogent Embedded Inc.
+ *
+ * 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; version 2 of the License.
+ */
+
+#include <linux/bug.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+
+#include <dt-bindings/power/r8a7792-sysc.h>
+
+#include "rcar-sysc.h"
+
+static const struct rcar_sysc_area r8a7792_areas[] __initconst = {
+ { "always-on", 0, 0, R8A7792_PD_ALWAYS_ON, -1, PD_ALWAYS_ON },
+ { "ca15-scu", 0x180, 0, R8A7792_PD_CA15_SCU, R8A7792_PD_ALWAYS_ON,
+ PD_SCU },
+ { "ca15-cpu0", 0x40, 0, R8A7792_PD_CA15_CPU0, R8A7792_PD_CA15_SCU,
+ PD_CPU_NOCR },
+ { "ca15-cpu1", 0x40, 1, R8A7792_PD_CA15_CPU1, R8A7792_PD_CA15_SCU,
+ PD_CPU_NOCR },
+ { "sgx", 0xc0, 0, R8A7792_PD_SGX, R8A7792_PD_ALWAYS_ON },
+ { "imp", 0x140, 0, R8A7792_PD_IMP, R8A7792_PD_ALWAYS_ON },
+};
+
+const struct rcar_sysc_info r8a7792_sysc_info __initconst = {
+ .areas = r8a7792_areas,
+ .num_areas = ARRAY_SIZE(r8a7792_areas),
+};
diff --git a/drivers/soc/renesas/r8a7796-sysc.c b/drivers/soc/renesas/r8a7796-sysc.c
new file mode 100644
index 000000000000..f700c842b9e1
--- /dev/null
+++ b/drivers/soc/renesas/r8a7796-sysc.c
@@ -0,0 +1,48 @@
+/*
+ * Renesas R-Car M3-W System Controller
+ *
+ * Copyright (C) 2016 Glider bvba
+ *
+ * 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; version 2 of the License.
+ */
+
+#include <linux/bug.h>
+#include <linux/kernel.h>
+
+#include <dt-bindings/power/r8a7796-sysc.h>
+
+#include "rcar-sysc.h"
+
+static const struct rcar_sysc_area r8a7796_areas[] __initconst = {
+ { "always-on", 0, 0, R8A7796_PD_ALWAYS_ON, -1, PD_ALWAYS_ON },
+ { "ca57-scu", 0x1c0, 0, R8A7796_PD_CA57_SCU, R8A7796_PD_ALWAYS_ON,
+ PD_SCU },
+ { "ca57-cpu0", 0x80, 0, R8A7796_PD_CA57_CPU0, R8A7796_PD_CA57_SCU,
+ PD_CPU_NOCR },
+ { "ca57-cpu1", 0x80, 1, R8A7796_PD_CA57_CPU1, R8A7796_PD_CA57_SCU,
+ PD_CPU_NOCR },
+ { "ca53-scu", 0x140, 0, R8A7796_PD_CA53_SCU, R8A7796_PD_ALWAYS_ON,
+ PD_SCU },
+ { "ca53-cpu0", 0x200, 0, R8A7796_PD_CA53_CPU0, R8A7796_PD_CA53_SCU,
+ PD_CPU_NOCR },
+ { "ca53-cpu1", 0x200, 1, R8A7796_PD_CA53_CPU1, R8A7796_PD_CA53_SCU,
+ PD_CPU_NOCR },
+ { "ca53-cpu2", 0x200, 2, R8A7796_PD_CA53_CPU2, R8A7796_PD_CA53_SCU,
+ PD_CPU_NOCR },
+ { "ca53-cpu3", 0x200, 3, R8A7796_PD_CA53_CPU3, R8A7796_PD_CA53_SCU,
+ PD_CPU_NOCR },
+ { "cr7", 0x240, 0, R8A7796_PD_CR7, R8A7796_PD_ALWAYS_ON },
+ { "a3vc", 0x380, 0, R8A7796_PD_A3VC, R8A7796_PD_ALWAYS_ON },
+ { "a2vc0", 0x3c0, 0, R8A7796_PD_A2VC0, R8A7796_PD_A3VC },
+ { "a2vc1", 0x3c0, 1, R8A7796_PD_A2VC1, R8A7796_PD_A3VC },
+ { "3dg-a", 0x100, 0, R8A7796_PD_3DG_A, R8A7796_PD_ALWAYS_ON },
+ { "3dg-b", 0x100, 1, R8A7796_PD_3DG_B, R8A7796_PD_3DG_A },
+ { "a3ir", 0x180, 0, R8A7796_PD_A3IR, R8A7796_PD_ALWAYS_ON },
+};
+
+const struct rcar_sysc_info r8a7796_sysc_info __initconst = {
+ .areas = r8a7796_areas,
+ .num_areas = ARRAY_SIZE(r8a7796_areas),
+};
diff --git a/drivers/soc/renesas/rcar-sysc.c b/drivers/soc/renesas/rcar-sysc.c
index 79dbc770895f..65c8e1eb90c0 100644
--- a/drivers/soc/renesas/rcar-sysc.c
+++ b/drivers/soc/renesas/rcar-sysc.c
@@ -164,15 +164,6 @@ static bool rcar_sysc_power_is_off(const struct rcar_sysc_ch *sysc_ch)
return false;
}
-void __iomem *rcar_sysc_init(phys_addr_t base)
-{
- rcar_sysc_base = ioremap_nocache(base, PAGE_SIZE);
- if (!rcar_sysc_base)
- panic("unable to ioremap R-Car SYSC hardware block\n");
-
- return rcar_sysc_base;
-}
-
struct rcar_sysc_pd {
struct generic_pm_domain genpd;
struct rcar_sysc_ch ch;
@@ -293,6 +284,9 @@ static const struct of_device_id rcar_sysc_matches[] = {
#ifdef CONFIG_ARCH_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 },
@@ -303,6 +297,9 @@ static const struct of_device_id rcar_sysc_matches[] = {
#ifdef CONFIG_ARCH_R8A7795
{ .compatible = "renesas,r8a7795-sysc", .data = &r8a7795_sysc_info },
#endif
+#ifdef CONFIG_ARCH_R8A7796
+ { .compatible = "renesas,r8a7796-sysc", .data = &r8a7796_sysc_info },
+#endif
{ /* sentinel */ }
};
@@ -322,6 +319,9 @@ static int __init rcar_sysc_pd_init(void)
unsigned int i;
int error;
+ if (rcar_sysc_base)
+ return 0;
+
np = of_find_matching_node_and_match(NULL, rcar_sysc_matches, &match);
if (!np)
return -ENODEV;
@@ -392,10 +392,35 @@ static int __init rcar_sysc_pd_init(void)
domains->domains[area->isr_bit] = &pd->genpd;
}
- of_genpd_add_provider_onecell(np, &domains->onecell_data);
+ error = of_genpd_add_provider_onecell(np, &domains->onecell_data);
out_put:
of_node_put(np);
return error;
}
early_initcall(rcar_sysc_pd_init);
+
+void __init rcar_sysc_init(phys_addr_t base, u32 syscier)
+{
+ u32 syscimr;
+
+ if (!rcar_sysc_pd_init())
+ return;
+
+ rcar_sysc_base = ioremap_nocache(base, PAGE_SIZE);
+
+ /*
+ * Mask all interrupt sources to prevent the CPU from receiving them.
+ * Make sure not to clear reserved bits that were set before.
+ */
+ syscimr = ioread32(rcar_sysc_base + SYSCIMR);
+ syscimr |= syscier;
+ pr_debug("%s: syscimr = 0x%08x\n", __func__, syscimr);
+ iowrite32(syscimr, rcar_sysc_base + SYSCIMR);
+
+ /*
+ * SYSC needs all interrupt sources enabled to control power.
+ */
+ pr_debug("%s: syscier = 0x%08x\n", __func__, syscier);
+ iowrite32(syscier, rcar_sysc_base + SYSCIER);
+}
diff --git a/drivers/soc/renesas/rcar-sysc.h b/drivers/soc/renesas/rcar-sysc.h
index 5e766174c2f4..77dbe861473f 100644
--- a/drivers/soc/renesas/rcar-sysc.h
+++ b/drivers/soc/renesas/rcar-sysc.h
@@ -53,6 +53,8 @@ struct rcar_sysc_info {
extern const struct rcar_sysc_info r8a7779_sysc_info;
extern const struct rcar_sysc_info r8a7790_sysc_info;
extern const struct rcar_sysc_info r8a7791_sysc_info;
+extern const struct rcar_sysc_info r8a7792_sysc_info;
extern const struct rcar_sysc_info r8a7794_sysc_info;
extern const struct rcar_sysc_info r8a7795_sysc_info;
+extern const struct rcar_sysc_info r8a7796_sysc_info;
#endif /* __SOC_RENESAS_RCAR_SYSC_H__ */
diff --git a/drivers/soc/samsung/Kconfig b/drivers/soc/samsung/Kconfig
index d7fc123006a3..245533907d1b 100644
--- a/drivers/soc/samsung/Kconfig
+++ b/drivers/soc/samsung/Kconfig
@@ -10,4 +10,8 @@ config EXYNOS_PMU
bool "Exynos PMU controller driver" if COMPILE_TEST
depends on (ARM && ARCH_EXYNOS) || ((ARM || ARM64) && COMPILE_TEST)
+config EXYNOS_PM_DOMAINS
+ bool "Exynos PM domains" if COMPILE_TEST
+ depends on PM_GENERIC_DOMAINS || COMPILE_TEST
+
endif
diff --git a/drivers/soc/samsung/Makefile b/drivers/soc/samsung/Makefile
index f64ac4d80564..3619f2ecddaa 100644
--- a/drivers/soc/samsung/Makefile
+++ b/drivers/soc/samsung/Makefile
@@ -1,2 +1,3 @@
obj-$(CONFIG_EXYNOS_PMU) += exynos-pmu.o exynos3250-pmu.o exynos4-pmu.o \
exynos5250-pmu.o exynos5420-pmu.o
+obj-$(CONFIG_EXYNOS_PM_DOMAINS) += pm_domains.o
diff --git a/drivers/soc/samsung/pm_domains.c b/drivers/soc/samsung/pm_domains.c
new file mode 100644
index 000000000000..f60515eefb66
--- /dev/null
+++ b/drivers/soc/samsung/pm_domains.c
@@ -0,0 +1,245 @@
+/*
+ * Exynos Generic power domain support.
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Implementation of Exynos specific power domain control which is used in
+ * conjunction with runtime-pm. Support for both device-tree and non-device-tree
+ * based power domain support is included.
+ *
+ * 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/io.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/pm_domain.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/sched.h>
+
+#define MAX_CLK_PER_DOMAIN 4
+
+struct exynos_pm_domain_config {
+ /* Value for LOCAL_PWR_CFG and STATUS fields for each domain */
+ u32 local_pwr_cfg;
+};
+
+/*
+ * Exynos specific wrapper around the generic power domain
+ */
+struct exynos_pm_domain {
+ void __iomem *base;
+ char const *name;
+ bool is_off;
+ struct generic_pm_domain pd;
+ struct clk *oscclk;
+ struct clk *clk[MAX_CLK_PER_DOMAIN];
+ struct clk *pclk[MAX_CLK_PER_DOMAIN];
+ struct clk *asb_clk[MAX_CLK_PER_DOMAIN];
+ u32 local_pwr_cfg;
+};
+
+static int exynos_pd_power(struct generic_pm_domain *domain, bool power_on)
+{
+ struct exynos_pm_domain *pd;
+ void __iomem *base;
+ u32 timeout, pwr;
+ char *op;
+ int i;
+
+ pd = container_of(domain, struct exynos_pm_domain, pd);
+ base = pd->base;
+
+ for (i = 0; i < MAX_CLK_PER_DOMAIN; i++) {
+ if (IS_ERR(pd->asb_clk[i]))
+ break;
+ clk_prepare_enable(pd->asb_clk[i]);
+ }
+
+ /* Set oscclk before powering off a domain*/
+ if (!power_on) {
+ for (i = 0; i < MAX_CLK_PER_DOMAIN; i++) {
+ if (IS_ERR(pd->clk[i]))
+ break;
+ pd->pclk[i] = clk_get_parent(pd->clk[i]);
+ if (clk_set_parent(pd->clk[i], pd->oscclk))
+ pr_err("%s: error setting oscclk as parent to clock %d\n",
+ pd->name, i);
+ }
+ }
+
+ pwr = power_on ? pd->local_pwr_cfg : 0;
+ __raw_writel(pwr, base);
+
+ /* Wait max 1ms */
+ timeout = 10;
+
+ while ((__raw_readl(base + 0x4) & pd->local_pwr_cfg) != pwr) {
+ if (!timeout) {
+ op = (power_on) ? "enable" : "disable";
+ pr_err("Power domain %s %s failed\n", domain->name, op);
+ return -ETIMEDOUT;
+ }
+ timeout--;
+ cpu_relax();
+ usleep_range(80, 100);
+ }
+
+ /* Restore clocks after powering on a domain*/
+ if (power_on) {
+ for (i = 0; i < MAX_CLK_PER_DOMAIN; i++) {
+ if (IS_ERR(pd->clk[i]))
+ break;
+
+ if (IS_ERR(pd->pclk[i]))
+ continue; /* Skip on first power up */
+ if (clk_set_parent(pd->clk[i], pd->pclk[i]))
+ pr_err("%s: error setting parent to clock%d\n",
+ pd->name, i);
+ }
+ }
+
+ for (i = 0; i < MAX_CLK_PER_DOMAIN; i++) {
+ if (IS_ERR(pd->asb_clk[i]))
+ break;
+ clk_disable_unprepare(pd->asb_clk[i]);
+ }
+
+ return 0;
+}
+
+static int exynos_pd_power_on(struct generic_pm_domain *domain)
+{
+ return exynos_pd_power(domain, true);
+}
+
+static int exynos_pd_power_off(struct generic_pm_domain *domain)
+{
+ return exynos_pd_power(domain, false);
+}
+
+static const struct exynos_pm_domain_config exynos4210_cfg __initconst = {
+ .local_pwr_cfg = 0x7,
+};
+
+static const struct of_device_id exynos_pm_domain_of_match[] __initconst = {
+ {
+ .compatible = "samsung,exynos4210-pd",
+ .data = &exynos4210_cfg,
+ },
+ { },
+};
+
+static __init int exynos4_pm_init_power_domain(void)
+{
+ struct device_node *np;
+ const struct of_device_id *match;
+
+ for_each_matching_node_and_match(np, exynos_pm_domain_of_match, &match) {
+ const struct exynos_pm_domain_config *pm_domain_cfg;
+ struct exynos_pm_domain *pd;
+ int on, i;
+
+ pm_domain_cfg = match->data;
+
+ pd = kzalloc(sizeof(*pd), GFP_KERNEL);
+ if (!pd) {
+ pr_err("%s: failed to allocate memory for domain\n",
+ __func__);
+ of_node_put(np);
+ return -ENOMEM;
+ }
+ pd->pd.name = kstrdup_const(strrchr(np->full_name, '/') + 1,
+ GFP_KERNEL);
+ if (!pd->pd.name) {
+ kfree(pd);
+ of_node_put(np);
+ return -ENOMEM;
+ }
+
+ pd->name = pd->pd.name;
+ pd->base = of_iomap(np, 0);
+ if (!pd->base) {
+ pr_warn("%s: failed to map memory\n", __func__);
+ kfree_const(pd->pd.name);
+ kfree(pd);
+ continue;
+ }
+
+ pd->pd.power_off = exynos_pd_power_off;
+ pd->pd.power_on = exynos_pd_power_on;
+ pd->local_pwr_cfg = pm_domain_cfg->local_pwr_cfg;
+
+ for (i = 0; i < MAX_CLK_PER_DOMAIN; i++) {
+ char clk_name[8];
+
+ snprintf(clk_name, sizeof(clk_name), "asb%d", i);
+ pd->asb_clk[i] = of_clk_get_by_name(np, clk_name);
+ if (IS_ERR(pd->asb_clk[i]))
+ break;
+ }
+
+ pd->oscclk = of_clk_get_by_name(np, "oscclk");
+ if (IS_ERR(pd->oscclk))
+ goto no_clk;
+
+ for (i = 0; i < MAX_CLK_PER_DOMAIN; i++) {
+ char clk_name[8];
+
+ snprintf(clk_name, sizeof(clk_name), "clk%d", i);
+ pd->clk[i] = of_clk_get_by_name(np, clk_name);
+ if (IS_ERR(pd->clk[i]))
+ break;
+ /*
+ * Skip setting parent on first power up.
+ * The parent at this time may not be useful at all.
+ */
+ pd->pclk[i] = ERR_PTR(-EINVAL);
+ }
+
+ if (IS_ERR(pd->clk[0]))
+ clk_put(pd->oscclk);
+
+no_clk:
+ on = __raw_readl(pd->base + 0x4) & pd->local_pwr_cfg;
+
+ pm_genpd_init(&pd->pd, NULL, !on);
+ of_genpd_add_provider_simple(np, &pd->pd);
+ }
+
+ /* Assign the child power domains to their parents */
+ for_each_matching_node(np, exynos_pm_domain_of_match) {
+ struct generic_pm_domain *child_domain, *parent_domain;
+ struct of_phandle_args args;
+
+ args.np = np;
+ args.args_count = 0;
+ child_domain = of_genpd_get_from_provider(&args);
+ if (IS_ERR(child_domain))
+ continue;
+
+ if (of_parse_phandle_with_args(np, "power-domains",
+ "#power-domain-cells", 0, &args) != 0)
+ continue;
+
+ parent_domain = of_genpd_get_from_provider(&args);
+ if (IS_ERR(parent_domain))
+ continue;
+
+ if (pm_genpd_add_subdomain(parent_domain, child_domain))
+ pr_warn("%s failed to add subdomain: %s\n",
+ parent_domain->name, child_domain->name);
+ else
+ pr_info("%s has as child subdomain: %s.\n",
+ parent_domain->name, child_domain->name);
+ }
+
+ return 0;
+}
+core_initcall(exynos4_pm_init_power_domain);
diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
index bb173456bbff..71c834f3847e 100644
--- a/drivers/soc/tegra/pmc.c
+++ b/drivers/soc/tegra/pmc.c
@@ -51,6 +51,7 @@
#define PMC_CNTRL_CPU_PWRREQ_POLARITY (1 << 15) /* CPU pwr req polarity */
#define PMC_CNTRL_CPU_PWRREQ_OE (1 << 16) /* CPU pwr req enable */
#define PMC_CNTRL_INTR_POLARITY (1 << 17) /* inverts INTR polarity */
+#define PMC_CNTRL_MAIN_RST (1 << 4)
#define DPD_SAMPLE 0x020
#define DPD_SAMPLE_ENABLE (1 << 0)
@@ -80,6 +81,14 @@
#define PMC_SENSOR_CTRL_SCRATCH_WRITE (1 << 2)
#define PMC_SENSOR_CTRL_ENABLE_RST (1 << 1)
+#define PMC_RST_STATUS 0x1b4
+#define PMC_RST_STATUS_POR 0
+#define PMC_RST_STATUS_WATCHDOG 1
+#define PMC_RST_STATUS_SENSOR 2
+#define PMC_RST_STATUS_SW_MAIN 3
+#define PMC_RST_STATUS_LP0 4
+#define PMC_RST_STATUS_AOTAG 5
+
#define IO_DPD_REQ 0x1b8
#define IO_DPD_REQ_CODE_IDLE (0 << 30)
#define IO_DPD_REQ_CODE_OFF (1 << 30)
@@ -399,6 +408,7 @@ static int tegra_powergate_power_up(struct tegra_powergate *pg,
disable_clks:
tegra_powergate_disable_clocks(pg);
usleep_range(10, 20);
+
powergate_off:
tegra_powergate_set(pg->id, false);
@@ -436,6 +446,7 @@ assert_resets:
usleep_range(10, 20);
tegra_powergate_reset_deassert(pg);
usleep_range(10, 20);
+
disable_clks:
tegra_powergate_disable_clocks(pg);
@@ -540,6 +551,9 @@ int tegra_powergate_sequence_power_up(unsigned int id, struct clk *clk,
struct tegra_powergate pg;
int err;
+ if (!tegra_powergate_is_available(id))
+ return -EINVAL;
+
pg.id = id;
pg.clks = &clk;
pg.num_clks = 1;
@@ -638,9 +652,10 @@ static int tegra_pmc_restart_notify(struct notifier_block *this,
tegra_pmc_writel(value, PMC_SCRATCH0);
- value = tegra_pmc_readl(0);
- value |= 0x10;
- tegra_pmc_writel(value, 0);
+ /* reset everything but PMC_SCRATCH0 and PMC_RST_STATUS */
+ value = tegra_pmc_readl(PMC_CNTRL);
+ value |= PMC_CNTRL_MAIN_RST;
+ tegra_pmc_writel(value, PMC_CNTRL);
return NOTIFY_DONE;
}
@@ -722,13 +737,14 @@ static int tegra_powergate_of_get_clks(struct tegra_powergate *pg,
err:
while (i--)
clk_put(pg->clks[i]);
+
kfree(pg->clks);
return err;
}
static int tegra_powergate_of_get_resets(struct tegra_powergate *pg,
- struct device_node *np)
+ struct device_node *np, bool off)
{
struct reset_control *rst;
unsigned int i, count;
@@ -748,6 +764,16 @@ static int tegra_powergate_of_get_resets(struct tegra_powergate *pg,
err = PTR_ERR(pg->resets[i]);
goto error;
}
+
+ if (off)
+ err = reset_control_assert(pg->resets[i]);
+ else
+ err = reset_control_deassert(pg->resets[i]);
+
+ if (err) {
+ reset_control_put(pg->resets[i]);
+ goto error;
+ }
}
pg->num_resets = count;
@@ -757,6 +783,7 @@ static int tegra_powergate_of_get_resets(struct tegra_powergate *pg,
error:
while (i--)
reset_control_put(pg->resets[i]);
+
kfree(pg->resets);
return err;
@@ -765,16 +792,19 @@ error:
static void tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np)
{
struct tegra_powergate *pg;
+ int id, err;
bool off;
- int id;
pg = kzalloc(sizeof(*pg), GFP_KERNEL);
if (!pg)
- goto error;
+ return;
id = tegra_powergate_lookup(pmc, np->name);
- if (id < 0)
+ if (id < 0) {
+ dev_err(pmc->dev, "powergate lookup failed for %s: %d\n",
+ np->name, id);
goto free_mem;
+ }
/*
* Clear the bit for this powergate so it cannot be managed
@@ -788,31 +818,64 @@ static void tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np)
pg->genpd.power_on = tegra_genpd_power_on;
pg->pmc = pmc;
- if (tegra_powergate_of_get_clks(pg, np))
+ off = !tegra_powergate_is_powered(pg->id);
+
+ err = tegra_powergate_of_get_clks(pg, np);
+ if (err < 0) {
+ dev_err(pmc->dev, "failed to get clocks for %s: %d\n",
+ np->name, err);
goto set_available;
+ }
- if (tegra_powergate_of_get_resets(pg, np))
+ err = tegra_powergate_of_get_resets(pg, np, off);
+ if (err < 0) {
+ dev_err(pmc->dev, "failed to get resets for %s: %d\n",
+ np->name, err);
goto remove_clks;
+ }
- off = !tegra_powergate_is_powered(pg->id);
+ if (!IS_ENABLED(CONFIG_PM_GENERIC_DOMAINS))
+ goto power_on_cleanup;
+
+ /*
+ * FIXME: If XHCI is enabled for Tegra, then power-up the XUSB
+ * host and super-speed partitions. Once the XHCI driver
+ * manages the partitions itself this code can be removed. Note
+ * that we don't register these partitions with the genpd core
+ * to avoid it from powering down the partitions as they appear
+ * to be unused.
+ */
+ if (IS_ENABLED(CONFIG_USB_XHCI_TEGRA) &&
+ (id == TEGRA_POWERGATE_XUSBA || id == TEGRA_POWERGATE_XUSBC))
+ goto power_on_cleanup;
pm_genpd_init(&pg->genpd, NULL, off);
- if (of_genpd_add_provider_simple(np, &pg->genpd))
+ err = of_genpd_add_provider_simple(np, &pg->genpd);
+ if (err < 0) {
+ dev_err(pmc->dev, "failed to add genpd provider for %s: %d\n",
+ np->name, err);
goto remove_resets;
+ }
dev_dbg(pmc->dev, "added power domain %s\n", pg->genpd.name);
return;
+power_on_cleanup:
+ if (off)
+ WARN_ON(tegra_powergate_power_up(pg, true));
+
remove_resets:
while (pg->num_resets--)
reset_control_put(pg->resets[pg->num_resets]);
+
kfree(pg->resets);
remove_clks:
while (pg->num_clks--)
clk_put(pg->clks[pg->num_clks]);
+
kfree(pg->clks);
set_available:
@@ -820,16 +883,20 @@ set_available:
free_mem:
kfree(pg);
-
-error:
- dev_err(pmc->dev, "failed to create power domain for %s\n", np->name);
}
-static void tegra_powergate_init(struct tegra_pmc *pmc)
+static void tegra_powergate_init(struct tegra_pmc *pmc,
+ struct device_node *parent)
{
struct device_node *np, *child;
+ unsigned int i;
+
+ /* Create a bitmap of the available and valid partitions */
+ for (i = 0; i < pmc->soc->num_powergates; i++)
+ if (pmc->soc->powergates[i])
+ set_bit(i, pmc->powergates_available);
- np = of_get_child_by_name(pmc->dev->of_node, "powergates");
+ np = of_get_child_by_name(parent, "powergates");
if (!np)
return;
@@ -1205,6 +1272,14 @@ static int tegra_pmc_probe(struct platform_device *pdev)
struct resource *res;
int err;
+ /*
+ * Early initialisation should have configured an initial
+ * register mapping and setup the soc data pointer. If these
+ * are not valid then something went badly wrong!
+ */
+ if (WARN_ON(!pmc->base || !pmc->soc))
+ return -ENODEV;
+
err = tegra_pmc_parse_dt(pmc, pdev->dev.of_node);
if (err < 0)
return err;
@@ -1242,8 +1317,6 @@ static int tegra_pmc_probe(struct platform_device *pdev)
return err;
}
- tegra_powergate_init(pmc);
-
mutex_lock(&pmc->powergates_lock);
iounmap(pmc->base);
pmc->base = base;
@@ -1477,10 +1550,11 @@ static int __init tegra_pmc_early_init(void)
const struct of_device_id *match;
struct device_node *np;
struct resource regs;
- unsigned int i;
bool invert;
u32 value;
+ mutex_init(&pmc->powergates_lock);
+
np = of_find_matching_node_and_match(NULL, tegra_pmc_match, &match);
if (!np) {
/*
@@ -1515,39 +1589,40 @@ static int __init tegra_pmc_early_init(void)
*/
if (of_address_to_resource(np, 0, &regs) < 0) {
pr_err("failed to get PMC registers\n");
+ of_node_put(np);
return -ENXIO;
}
-
- pmc->soc = match->data;
}
pmc->base = ioremap_nocache(regs.start, resource_size(&regs));
if (!pmc->base) {
pr_err("failed to map PMC registers\n");
+ of_node_put(np);
return -ENXIO;
}
- /* Create a bit-map of the available and valid partitions */
- for (i = 0; i < pmc->soc->num_powergates; i++)
- if (pmc->soc->powergates[i])
- set_bit(i, pmc->powergates_available);
+ if (np) {
+ pmc->soc = match->data;
- mutex_init(&pmc->powergates_lock);
+ tegra_powergate_init(pmc, np);
- /*
- * Invert the interrupt polarity if a PMC device tree node exists and
- * contains the nvidia,invert-interrupt property.
- */
- invert = of_property_read_bool(np, "nvidia,invert-interrupt");
+ /*
+ * Invert the interrupt polarity if a PMC device tree node
+ * exists and contains the nvidia,invert-interrupt property.
+ */
+ invert = of_property_read_bool(np, "nvidia,invert-interrupt");
- value = tegra_pmc_readl(PMC_CNTRL);
+ value = tegra_pmc_readl(PMC_CNTRL);
- if (invert)
- value |= PMC_CNTRL_INTR_POLARITY;
- else
- value &= ~PMC_CNTRL_INTR_POLARITY;
+ if (invert)
+ value |= PMC_CNTRL_INTR_POLARITY;
+ else
+ value &= ~PMC_CNTRL_INTR_POLARITY;
- tegra_pmc_writel(value, PMC_CNTRL);
+ tegra_pmc_writel(value, PMC_CNTRL);
+
+ of_node_put(np);
+ }
return 0;
}