Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1757197AbeAHWLT (ORCPT + 1 other); Mon, 8 Jan 2018 17:11:19 -0500 Received: from mail-by2nam03on0084.outbound.protection.outlook.com ([104.47.42.84]:11424 "EHLO NAM03-BY2-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1756391AbeAHWLR (ORCPT ); Mon, 8 Jan 2018 17:11:17 -0500 Authentication-Results: spf=pass (sender IP is 149.199.60.83) smtp.mailfrom=xilinx.com; gmail.com; dkim=none (message not signed) header.d=none;gmail.com; dmarc=bestguesspass action=none header.from=xilinx.com; From: Jolly Shah To: , , , , , , , CC: , , Jolly Shah , Rajan Vaja Subject: [RFC PATCH] drivers: soc: xilinx: Add ZynqMP PM driver Date: Mon, 8 Jan 2018 14:10:44 -0800 Message-ID: <1515449444-5274-1-git-send-email-jollys@xilinx.com> X-Mailer: git-send-email 2.7.4 X-TM-AS-Product-Ver: IMSS-7.1.0.1224-8.2.0.1013-23582.004 X-TM-AS-User-Approved-Sender: Yes;Yes X-EOPAttributedMessage: 0 X-MS-Office365-Filtering-HT: Tenant X-Forefront-Antispam-Report: CIP:149.199.60.83;IPV:NLI;CTRY:US;EFV:NLI;SFV:NSPM;SFS:(10009020)(396003)(39860400002)(39380400002)(376002)(346002)(2980300002)(438002)(199004)(189003)(50226002)(50466002)(48376002)(356003)(110136005)(9786002)(8936002)(54906003)(77096006)(16586007)(8676002)(63266004)(47776003)(316002)(107886003)(4326008)(6636002)(81166006)(36756003)(5660300001)(2906002)(6666003)(59450400001)(39060400002)(106466001)(72206003)(7696005)(2201001)(106002)(478600001)(81156014)(305945005)(36386004)(51416003)(107986001)(2101003);DIR:OUT;SFP:1101;SCL:1;SRVR:MWHPR02MB3390;H:xsj-pvapsmtpgw01;FPR:;SPF:Pass;PTR:unknown-60-83.xilinx.com;A:1;MX:1;LANG:en; X-Microsoft-Exchange-Diagnostics: 1;BL2NAM02FT041;1:b328g9W8enFVNao5uNPAzYahwWZp18hBAqXRMJMTEvHmBsa5RYDA/IIfLWA98pf934jyBuPXvCEKOPcMDKR917NO7owFGHZ1OhaBvt0ol4IcEplYMkp6y6pCdSrBMB3H MIME-Version: 1.0 Content-Type: text/plain X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: a45809f9-6f07-49fb-5cfb-08d556e4bb81 X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:(4534020)(4602075)(4627115)(201703031133081)(201702281549075)(5600026)(4604075)(4608076)(2017052603307)(7153060);SRVR:MWHPR02MB3390; X-Microsoft-Exchange-Diagnostics: 1;MWHPR02MB3390;3:VieVKcdJQC3epkRDYRGhVoksy0jM/UY/HOD8+pzbhzP734IL7oFrPKQasutThYlHVs4vGE9QlaQsZNJV8L8Dog9w+R9ApEu2wvs0vBwuxYmeaeD1xT7CNoJUsgkDyathqHnL4V6Tv8AUVw0eznsAGbrtTNGNP4AK06C5U7HAj852trPByGPT1YNMdLp8o1y/xqMPhZs1C9Q82ne9uPqdBRXfmgdYqngeUKdggaovJmcy7QxRp8kMFvV889gTxkUyv0Otv58c7Ua4KPoLe2XLZ3w2CswezscFg1TiYEBqnX0wJwQqkexwB8XSAmcsy+MRMVy+3QHYs7kZ9Ml1tcbGisVpQ/ypg7kPk1xu6ijkmWU=;25:RUwagqSuRmn/IM1n0KIi3yRmesSIuvHPl6JZNgv+2o4KR/cKbSmNXa+2Wlt4sCGaWf9SUHvOBn8nPz/CBPw47c3GwfExBj3BN/QNN/JKQqsUY2mp46tFUXBiFRIo7mB4ZnOFMdWl9xP/ql/FqvAygD4cxL85pBfbSkiwE2Pm0iR2HHDjWG5/JhdJUdvUpXuq4q+kL72Dy4CidUmcxom+NZMca+O1nJxiZYy+JSF9SL7hiwap71ODIVyJT//1zPpcxaqcI4kick+tPdykE9rFbS+HqwByNt96TEDWSfmnwSY64yhvxO45/4OJdOtJ3Nagi0YmYt51ryVVX/puFCsD2w== X-MS-TrafficTypeDiagnostic: MWHPR02MB3390: X-Microsoft-Exchange-Diagnostics: 1;MWHPR02MB3390;31:wqRE0d6tmA6dY7WBA4YSGU5GV69NABxXlZFKPlq6EfmvssF73kVf3R8kMZ62Gg99UlfLPe6f+TJZlg6g8/344mHgjUGi/uj/3OnvZiort3w9xqygLZlOU/ao1ExD0s35ZReuUql737xq+hlo2f8zzmAuAj8zkWK7b+qLZIKaO9sBRUB1CFx0KG22C91ilm3xXID4NdWPFjTVvMXW+pUfYptO5CaeM3JrRx2418v7Jlk=;20:35PLvj41VnxU3Fz7Cv7VVQTzaBz1nONWRiqh0P9RA0icQcFZNkkRtgLsJ4cgnlY6whrx9LepWe3/XorhFqgdDQ/zV48Fo1y8OTSRTupHDOMOdFl6WeNnbFAzGOk83UH3Yp4mZZmb4HrdWMNByMTTTw7cumrwwG5nFZ5lEeMNjra14g35tFdWz3nL85kAL67/r4iRa8D4sZlAgu5SkvoUoXMqpjZrI2IWg5NzYAxEsO/VGZOUTGMg9MGo/kHueBJx4+rWRabZJLzLNl5ZpigaNELXMtDP/Cj6KQ2vFRmTQ+uAIMmUdiRUukoer+3udTCd10xmZeY2GzXzbYZwDFTzEEuOzNey2CWvqlRpElyXBjtWy/4cTwXmhilfO+X5/zDsABP4fEt7b5R9VKjrY2lyDlKYgL7oKbw0f4ZzO+HUjxDPQqJ66UPXStbmczRezfEl5Uru3XKHIphbLe5DdrVtrIzRY191QtRWxjwVz0iveFZOXTp8bnulIQ/7IVUUTVpM X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:(192813158149592); X-Exchange-Antispam-Report-CFA-Test: BCL:0;PCL:0;RULEID:(6040470)(2401047)(5005006)(8121501046)(93006095)(93004095)(10201501046)(3002001)(3231023)(944501075)(6055026)(6041268)(20161123564045)(20161123562045)(20161123558120)(20161123560045)(201703131423095)(201702281528075)(20161123555045)(201703061421075)(201703061406153)(6072148)(201708071742011);SRVR:MWHPR02MB3390;BCL:0;PCL:0;RULEID:(100000803101)(100110400095);SRVR:MWHPR02MB3390; X-Microsoft-Exchange-Diagnostics: 1;MWHPR02MB3390;4:ncBIz49zNM/szc1mNAskmA6qTpp5CZQRMrJZpouGFdXjuDBBzieBYtOuObJ5e8s/jVr7v6br+Cu8BmVINWJw0p3f+XZOv5xpg6jdCX7mMxmVUmjH+n3Ddy4/4VR2IIcetw0wexAKIZfLU8cnfy+s8AXugN5C9ibYcyocIzBwNnIq6PLgzL4IMbYBqq6Q1Lf+lsC5kCMG7VCtTKOpHzTY6kYsL+++9s8oMJugVJxuxNv9FVaXVYyOaeNzwAjmG24z5odGLgx+GZYsnSS/gny3YPmeP/UiRAtaWcKa8wSzni5NBjtup6O6zfAheDHI5O3a X-Forefront-PRVS: 054642504A X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1;MWHPR02MB3390;23:v5/FtpEoJCsKuQ5f9+ew9ew9XKJV9zqZ9KVYGx+2C?= =?us-ascii?Q?WW4Mi6Ub9zVCYK3bbzT6LvfMi322eYh6dFieFDbqJGEeMtoaa+HmN6DTrmm/?= =?us-ascii?Q?4hrKuoFU2bCMggeVkdtU4xz6hju1e9tPjfT7HiCo7p+g79+FPQVKDwo3wNrS?= =?us-ascii?Q?tbPZobv/PdNsPXKK20rJ6TF+cEPXp5Xy5KvaBJzPWzwLicW/8ahtxMOsq2Qa?= =?us-ascii?Q?WG0NMQExeOcUQDf9DoA5yP7oLT1VkvDLwW9iLCAAwuk8MI8q+TSNVTVI3WIT?= =?us-ascii?Q?VsasH3D3IqFKbZvXG0F506N9wuMx/ytbnbYtswFXZCCbXseq0vJBjIg4xpzm?= =?us-ascii?Q?asc6of1RpyFXFXt/RQAB3L3BJbh6XVQihHzIZfk07543VGrWkIC9OZC0b1HP?= =?us-ascii?Q?2Tuk/oqaxVUb47AtTupK1FYisCMS2pJ1ea9N/oyPaBZASSrUrHzmwUTDy84o?= =?us-ascii?Q?bTpNKWjGY6I5hn17N65JFTTq0N37ph7l/CpkKNZeac2lk+E6eHwJj8xyjbPe?= =?us-ascii?Q?gOLQt1mTs176c59QrQPNAgbKZEaMXuILMjm6+FetERfylS5slykkVGAYYDPS?= =?us-ascii?Q?DXq2fjetdJS0FovEVCscUfrw7CPh0+EKGJavnzStFIFje+YMi6Tv6WzFdNDi?= =?us-ascii?Q?BzCRy4FEM89KHx9w5uGmSCjWnKJ9G5fIpfFY+K01IB6nr36vSscXDRA2ZQ/3?= =?us-ascii?Q?8dVhG5U5hYAfe7Q8QM20kC9p48RDwIJrbWsXwZBeNp9JdjUxNW/O7oiJtaEP?= =?us-ascii?Q?e+HPveQdUC8FZ4CfmAhuI6nKeRb35m08YEOQCoqHUgeFLUPDw1luMGSPI8zG?= =?us-ascii?Q?j6zD9NvH+h8L45WGhlqeyWaT+jgW4oWZbkJ3RGGbrPqv+mg+rXCt2UNFZmCY?= =?us-ascii?Q?Hv8YA8k0Z8udVQyEjJq0gg5XVZ+xwrOGG0pnzdfNqbQPyANaXJyY9Hvec/KL?= =?us-ascii?Q?2bT8xgwC9QtsQsxdTxpjiuLlOnXXmS/oGgrb2AXkspN81ud0AQ4gTqci18JE?= =?us-ascii?Q?8erV7fNZrQr/YoOX30O93LJwAnNgX6yo4n1derEU/H/b/EwEOd7t6T0qigd2?= =?us-ascii?Q?e6Khf4=3D?= X-Microsoft-Exchange-Diagnostics: 1;MWHPR02MB3390;6:NwTpROunoPcIbZoriXDJF4WTDclEI9UikYjbkyjek93NcbvMzQqx/preBXOQ7ONVPiSvoG3ZA1abpKQ51XwO2Eau3nux4k8dePBSzcawuNNxcnToeLvp2fxfCJ84fIEJh+HAzjj4UajS/P/KVRijSJSXfkLpoM5A6iZ8dOQeP6TzRiCjhSRBwsEw3TU6MYlK7burDzuX6Hkdo/tOzZHqtbjRJW2p0ZYHC/pB/s5yFENeSYtbNk3nKYC/wBu7KUhbj0tYwRr7T6mmgNT3i3PRCIPdOFO6KuFKNoXKK8CBYCWOvfKFLo54pvXk5vzZImGCpeeIMDHKCa31av1PZFrJj6mBK2xEi8usy5Fe54ff9/c=;5:amH9SouhLnOEyxXeiUpns+JQDx3nFw9dNRfalqOmr0Ul+D80l49V5zAmYh0AmNmE2dEE9RmiGK6Rm7hPtJ2w/ivWfm9MivLpfmQMl+WNWXdivzzrOO0SakExKbcGkpiLG5j/h3t3dJYI9nyGC3dArlW1QWLsrHv0C/rquxFT8qU=;24:5iAoOO7W6bXe55NhlvN92UglGB0NepEIcinvFUKNc3phr40FB0q+U1vLXfDDU6lmz9Lth5cEvlV9O0volkN44iCH/gS1fpL/TumDLJG5GGs=;7:0hPSv3dZYcboR1YMtr8FRQEA2Tavk4uA9xc9jMlrNwpZrrek7UySifBFvFBKXktVWYrbuGRsiSZ6szCXQG0KNf05H2pHOFSyJiNnSaMLofxV+6W5Ypi23SgsdMDmj9NkSYgZmWng/ces5dmprrh4SuteUPiZqfMKPivfSH/a6TXErXok8cJEGPjSKw5/FM5kOBwQzmBm2R3GSpf3JTgGraZZ4NtwuSK5yYEG44GO7S4ROyYagA6SjxCqg9jBW1IR SpamDiagnosticOutput: 1:99 SpamDiagnosticMetadata: NSPM X-OriginatorOrg: xilinx.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 08 Jan 2018 22:11:13.7861 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: a45809f9-6f07-49fb-5cfb-08d556e4bb81 X-MS-Exchange-CrossTenant-Id: 657af505-d5df-48d0-8300-c31994686c5c X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=657af505-d5df-48d0-8300-c31994686c5c;Ip=[149.199.60.83];Helo=[xsj-pvapsmtpgw01] X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: MWHPR02MB3390 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Return-Path: Add ZynqMP PM driver. PM driver provides power management support for ZynqMP. Signed-off-by: Jolly Shah Signed-off-by: Rajan Vaja --- .../bindings/soc/xilinx/xlnx,zynqmp-pm.txt | 15 ++ drivers/soc/Kconfig | 1 + drivers/soc/Makefile | 1 + drivers/soc/xilinx/Kconfig | 4 + drivers/soc/xilinx/Makefile | 4 + drivers/soc/xilinx/zynqmp/Kconfig | 15 ++ drivers/soc/xilinx/zynqmp/Makefile | 1 + drivers/soc/xilinx/zynqmp/pm.c | 265 +++++++++++++++++++++ 8 files changed, 306 insertions(+) create mode 100644 Documentation/devicetree/bindings/soc/xilinx/xlnx,zynqmp-pm.txt create mode 100644 drivers/soc/xilinx/Kconfig create mode 100644 drivers/soc/xilinx/Makefile create mode 100644 drivers/soc/xilinx/zynqmp/Kconfig create mode 100644 drivers/soc/xilinx/zynqmp/Makefile create mode 100644 drivers/soc/xilinx/zynqmp/pm.c diff --git a/Documentation/devicetree/bindings/soc/xilinx/xlnx,zynqmp-pm.txt b/Documentation/devicetree/bindings/soc/xilinx/xlnx,zynqmp-pm.txt new file mode 100644 index 0000000..9cfb40d --- /dev/null +++ b/Documentation/devicetree/bindings/soc/xilinx/xlnx,zynqmp-pm.txt @@ -0,0 +1,15 @@ +Xilinx Zynq MPSoC Power Management Device Tree Bindings + +The zynqmp-pm node describes the power management configurations. + +Required properties: + - compatible : Must contain: "xlnx,zynqmp-pm" + - interrupt-parent : Interrupt controller the interrupt is routed through + - interrupts : Interrupt specifier + +Examples: + zynqmp-firmware { + compatible = "xlnx,zynqmp-pm"; + interrupt-parent = <&gic>; + interrupts = <0 35 4>; + }; diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig index fc9e980..c07b4a8 100644 --- a/drivers/soc/Kconfig +++ b/drivers/soc/Kconfig @@ -16,6 +16,7 @@ source "drivers/soc/tegra/Kconfig" source "drivers/soc/ti/Kconfig" source "drivers/soc/ux500/Kconfig" source "drivers/soc/versatile/Kconfig" +source "drivers/soc/xilinx/Kconfig" source "drivers/soc/zte/Kconfig" endmenu diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile index deecb16..abb019a 100644 --- a/drivers/soc/Makefile +++ b/drivers/soc/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ obj-$(CONFIG_SOC_SAMSUNG) += samsung/ obj-$(CONFIG_ARCH_SUNXI) += sunxi/ obj-$(CONFIG_ARCH_TEGRA) += tegra/ +obj-$(CONFIG_ARCH_ZYNQMP) += xilinx/ obj-$(CONFIG_SOC_TI) += ti/ obj-$(CONFIG_ARCH_U8500) += ux500/ obj-$(CONFIG_PLAT_VERSATILE) += versatile/ diff --git a/drivers/soc/xilinx/Kconfig b/drivers/soc/xilinx/Kconfig new file mode 100644 index 0000000..190add7 --- /dev/null +++ b/drivers/soc/xilinx/Kconfig @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Kconfig for Xilinx SoCs + +source "drivers/soc/xilinx/zynqmp/Kconfig" diff --git a/drivers/soc/xilinx/Makefile b/drivers/soc/xilinx/Makefile new file mode 100644 index 0000000..bc9d560 --- /dev/null +++ b/drivers/soc/xilinx/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Makefile for Xilinx SoCs + +obj-$(CONFIG_ARCH_ZYNQMP) += zynqmp/ diff --git a/drivers/soc/xilinx/zynqmp/Kconfig b/drivers/soc/xilinx/zynqmp/Kconfig new file mode 100644 index 0000000..d3c784d --- /dev/null +++ b/drivers/soc/xilinx/zynqmp/Kconfig @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Kconfig for Xilinx zynqmp SoC +# +menu "Zynq MPSoC SoC Drivers" + depends on ARCH_ZYNQMP + + +config ZYNQMP_PM + bool "Enable Xilinx Zynq MPSoC Power Management" + depends on PM + help + Say yes to enable power management support for + ZyqnMP SoC. In doubt, say N. + +endmenu diff --git a/drivers/soc/xilinx/zynqmp/Makefile b/drivers/soc/xilinx/zynqmp/Makefile new file mode 100644 index 0000000..98034f7 --- /dev/null +++ b/drivers/soc/xilinx/zynqmp/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_ZYNQMP_PM) += pm.o diff --git a/drivers/soc/xilinx/zynqmp/pm.c b/drivers/soc/xilinx/zynqmp/pm.c new file mode 100644 index 0000000..7178fb5 --- /dev/null +++ b/drivers/soc/xilinx/zynqmp/pm.c @@ -0,0 +1,265 @@ +/* + * Xilinx Zynq MPSoC Power Management + * + * Copyright (C) 2014-2017 Xilinx, Inc. + * + * Davorin Mista + * Jolly Shah + * Rajan Vaja + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "zynqmp_pm" + +/** + * struct zynqmp_pm_work_struct - Wrapper for struct work_struct + * @callback_work: Work structure + * @args: Callback arguments + */ +struct zynqmp_pm_work_struct { + struct work_struct callback_work; + u32 args[CB_ARG_CNT]; +}; + +static struct zynqmp_pm_work_struct *zynqmp_pm_init_suspend_work; + +enum pm_suspend_mode { + PM_SUSPEND_MODE_STD, + PM_SUSPEND_MODE_POWER_OFF, +}; + +#define PM_SUSPEND_MODE_FIRST PM_SUSPEND_MODE_STD + +static const char *const suspend_modes[] = { + [PM_SUSPEND_MODE_STD] = "standard", + [PM_SUSPEND_MODE_POWER_OFF] = "power-off", +}; + +static enum pm_suspend_mode suspend_mode = PM_SUSPEND_MODE_STD; + +enum pm_api_cb_id { + PM_INIT_SUSPEND_CB = 30, + PM_ACKNOWLEDGE_CB, + PM_NOTIFY_CB, +}; + +static irqreturn_t zynqmp_pm_isr(int irq, void *data) +{ + u32 payload[CB_PAYLOAD_SIZE]; + const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops(); + + if (!eemi_ops || !eemi_ops->get_callback_data) + return IRQ_NONE; + + eemi_ops->get_callback_data(payload); + + if (!payload[0]) + return IRQ_NONE; + + /* First element is callback API ID, others are callback arguments */ + if (payload[0] == PM_INIT_SUSPEND_CB) { + if (work_pending(&zynqmp_pm_init_suspend_work->callback_work)) + goto done; + + /* Copy callback arguments into work's structure */ + memcpy(zynqmp_pm_init_suspend_work->args, &payload[1], + sizeof(zynqmp_pm_init_suspend_work->args)); + + queue_work(system_unbound_wq, + &zynqmp_pm_init_suspend_work->callback_work); + } + +done: + return IRQ_HANDLED; +} + +static const struct of_device_id pm_of_match[] = { + { .compatible = "xlnx,zynqmp-pm", }, + { /* end of table */ }, +}; + +MODULE_DEVICE_TABLE(of, pm_of_match); + +/** + * zynqmp_pm_init_suspend_work_fn - Initialize suspend + * @work: Pointer to work_struct + * + * Bottom-half of PM callback IRQ handler. + */ +static void zynqmp_pm_init_suspend_work_fn(struct work_struct *work) +{ + struct zynqmp_pm_work_struct *pm_work = + container_of(work, struct zynqmp_pm_work_struct, callback_work); + + if (pm_work->args[0] == ZYNQMP_PM_SUSPEND_REASON_SYSTEM_SHUTDOWN) { + orderly_poweroff(true); + } else if (pm_work->args[0] == + ZYNQMP_PM_SUSPEND_REASON_POWER_UNIT_REQUEST) { + pm_suspend(PM_SUSPEND_MEM); + } else { + pr_err("%s Unsupported InitSuspendCb reason code %d.\n" + , __func__, pm_work->args[0]); + } +} + +static ssize_t suspend_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char *s = buf; + int md; + + for (md = PM_SUSPEND_MODE_FIRST; md < ARRAY_SIZE(suspend_modes); md++) + if (suspend_modes[md]) { + if (md == suspend_mode) + s += sprintf(s, "[%s] ", suspend_modes[md]); + else + s += sprintf(s, "%s ", suspend_modes[md]); + } + + /* Convert last space to newline */ + if (s != buf) + *(s - 1) = '\n'; + return (s - buf); +} + +static ssize_t suspend_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int md, ret = -EINVAL; + const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops(); + + if (!eemi_ops || !eemi_ops->set_suspend_mode) + return ret; + + for (md = PM_SUSPEND_MODE_FIRST; md < ARRAY_SIZE(suspend_modes); md++) + if (suspend_modes[md] && + sysfs_streq(suspend_modes[md], buf)) { + ret = 0; + break; + } + + if (!ret && md != suspend_mode) { + ret = eemi_ops->set_suspend_mode(md); + if (likely(!ret)) + suspend_mode = md; + } + + return ret ? ret : count; +} + +static DEVICE_ATTR_RW(suspend_mode); + +/** + * zynqmp_pm_sysfs_init - Initialize PM driver sysfs interface + * @dev: Pointer to device structure + * + * Return: 0 on success, negative error code otherwise + */ +static int zynqmp_pm_sysfs_init(struct device *dev) +{ + return sysfs_create_file(&dev->kobj, &dev_attr_suspend_mode.attr); +} + +/** + * zynqmp_pm_probe - Probe existence of the PMU Firmware + * and initialize debugfs interface + * + * @pdev: Pointer to the platform_device structure + * + * Return: Returns 0 on success + * Negative error code otherwise + */ +static int zynqmp_pm_probe(struct platform_device *pdev) +{ + int ret, irq; + u32 pm_api_version; + const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops(); + + if (!eemi_ops || !eemi_ops->get_api_version) + return -ENXIO; + + eemi_ops->get_api_version(&pm_api_version); + + /* Check PM API version number */ + if (pm_api_version != ZYNQMP_PM_VERSION) + return -ENODEV; + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) + return -ENXIO; + + ret = request_irq(irq, zynqmp_pm_isr, IRQF_SHARED, DRIVER_NAME, pdev); + if (ret) { + dev_err(&pdev->dev, "request_irq '%d' failed with %d\n", + irq, ret); + return ret; + } + + zynqmp_pm_init_suspend_work = + devm_kzalloc(&pdev->dev, sizeof(struct zynqmp_pm_work_struct), + GFP_KERNEL); + if (!zynqmp_pm_init_suspend_work) { + ret = -ENOMEM; + goto error; + } + + INIT_WORK(&zynqmp_pm_init_suspend_work->callback_work, + zynqmp_pm_init_suspend_work_fn); + + ret = zynqmp_pm_sysfs_init(&pdev->dev); + if (ret) { + dev_err(&pdev->dev, "unable to initialize sysfs interface\n"); + goto error; + } + + dev_info(&pdev->dev, "Power management API v%d.%d\n", + ZYNQMP_PM_VERSION_MAJOR, ZYNQMP_PM_VERSION_MINOR); + + return 0; + +error: + free_irq(irq, 0); + return ret; +} + +static struct platform_driver zynqmp_pm_platform_driver = { + .probe = zynqmp_pm_probe, + .driver = { + .name = DRIVER_NAME, + .of_match_table = pm_of_match, + }, +}; +builtin_platform_driver(zynqmp_pm_platform_driver); + +/** + * zynqmp_pm_init - Notify PM firmware that initialization is completed + * + * Return: Status returned from the PM firmware + */ +static int __init zynqmp_pm_init(void) +{ + const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops(); + + if (!eemi_ops || !eemi_ops->init_finalize) + return -ENXIO; + + return eemi_ops->init_finalize(); +} + +late_initcall_sync(zynqmp_pm_init); -- 2.7.4