From patchwork Fri Nov 24 14:36:40 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arnaud POULIQUEN X-Patchwork-Id: 10074149 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 3FC2B60375 for ; Fri, 24 Nov 2017 14:41:43 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 3028E2A36B for ; Fri, 24 Nov 2017 14:41:43 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 2511F2A371; Fri, 24 Nov 2017 14:41:43 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 3A34E2A36B for ; Fri, 24 Nov 2017 14:41:42 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753560AbdKXOlk (ORCPT ); Fri, 24 Nov 2017 09:41:40 -0500 Received: from mx08-00178001.pphosted.com ([91.207.212.93]:54739 "EHLO mx07-00178001.pphosted.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1753540AbdKXOiy (ORCPT ); Fri, 24 Nov 2017 09:38:54 -0500 Received: from pps.filterd (m0046660.ppops.net [127.0.0.1]) by mx08-.pphosted.com (8.16.0.21/8.16.0.21) with SMTP id vAOEYDif004902; Fri, 24 Nov 2017 15:38:51 +0100 Received: from beta.dmz-eu.st.com (beta.dmz-eu.st.com [164.129.1.35]) by mx08-00178001.pphosted.com with ESMTP id 2edqjjj0r0-1 (version=TLSv1 cipher=ECDHE-RSA-AES256-SHA bits=256 verify=NOT); Fri, 24 Nov 2017 15:38:51 +0100 Received: from zeta.dmz-eu.st.com (zeta.dmz-eu.st.com [164.129.230.9]) by beta.dmz-eu.st.com (STMicroelectronics) with ESMTP id 710B238; Fri, 24 Nov 2017 14:38:50 +0000 (GMT) Received: from Webmail-eu.st.com (Safex1hubcas24.st.com [10.75.90.94]) by zeta.dmz-eu.st.com (STMicroelectronics) with ESMTP id 47BDE523A; Fri, 24 Nov 2017 14:38:50 +0000 (GMT) Received: from SAFEX1HUBCAS22.st.com (10.75.90.93) by Safex1hubcas24.st.com (10.75.90.94) with Microsoft SMTP Server (TLS) id 14.3.352.0; Fri, 24 Nov 2017 15:38:50 +0100 Received: from localhost (10.201.23.162) by Webmail-ga.st.com (10.75.90.48) with Microsoft SMTP Server (TLS) id 14.3.352.0; Fri, 24 Nov 2017 15:38:49 +0100 From: Arnaud Pouliquen To: Bjorn Andersson CC: , , "Loic PALLARDY" , Fabien DESSENNE , Suman Anna Subject: [RFC 4/6] remoteproc: add system resource manager device Date: Fri, 24 Nov 2017 15:36:40 +0100 Message-ID: <1511534202-12995-5-git-send-email-arnaud.pouliquen@st.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1511534202-12995-1-git-send-email-arnaud.pouliquen@st.com> References: <1511534202-12995-1-git-send-email-arnaud.pouliquen@st.com> MIME-Version: 1.0 X-Originating-IP: [10.201.23.162] X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:, , definitions=2017-11-24_05:, , signatures=0 Sender: linux-remoteproc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-remoteproc@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Fabien Dessenne The SRM device reserves and enables the resources needed by a remote processor. In this initial commit the only managed resources are: - clocks which are prepared and enabled - pins which are configured - regulators which are enabled Signed-off-by: Fabien Dessenne Signed-off-by: Arnaud Pouliquen Signed-off-by: Loic Pallardy --- drivers/remoteproc/Kconfig | 10 + drivers/remoteproc/Makefile | 1 + drivers/remoteproc/rproc_srm_dev.c | 462 +++++++++++++++++++++++++++++++++++++ 3 files changed, 473 insertions(+) create mode 100644 drivers/remoteproc/rproc_srm_dev.c diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index 4b9f187..a49589e 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -20,6 +20,16 @@ config REMOTEPROC_SRM_CORE The SRM handles resources allocated to remote processors. The core part is in charge of controlling the device children. +config REMOTEPROC_SRM_DEV + tristate "Remoteproc System Resource Manager device" + depends on REMOTEPROC_SRM_CORE + help + Say y here to enable the device driver of the remoteproc System + Resource Manager (SRM). + The SRM handles resources allocated to remote processors. + The device part is in charge of reserving and initializing resources + for a peripheral assigned to a coprocessor. + config IMX_REMOTEPROC tristate "IMX6/7 remoteproc support" depends on SOC_IMX6SX || SOC_IMX7D diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index 4ec05e3..36932da 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile @@ -10,6 +10,7 @@ remoteproc-y += remoteproc_sysfs.o remoteproc-y += remoteproc_virtio.o remoteproc-y += remoteproc_elf_loader.o obj-$(CONFIG_REMOTEPROC_SRM_CORE) += rproc_srm_core.o +obj-$(CONFIG_REMOTEPROC_SRM_DEV) += rproc_srm_dev.o obj-$(CONFIG_IMX_REMOTEPROC) += imx_rproc.o obj-$(CONFIG_OMAP_REMOTEPROC) += omap_remoteproc.o obj-$(CONFIG_WKUP_M3_RPROC) += wkup_m3_rproc.o diff --git a/drivers/remoteproc/rproc_srm_dev.c b/drivers/remoteproc/rproc_srm_dev.c new file mode 100644 index 0000000..4f76da2 --- /dev/null +++ b/drivers/remoteproc/rproc_srm_dev.c @@ -0,0 +1,462 @@ +/* + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved + * Authors: Fabien Dessenne . + * Arnaud Pouliquen + * Loic Pallardy + * + * License type: GPLv2 + * + * 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 in the hope that 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define SUPPLY_SUFFIX "-supply" + +struct rproc_srm_clk_info { + struct list_head list; + unsigned int index; + struct clk *clk; + const char *name; + bool enabled; +}; + +struct rproc_srm_pin_info { + struct list_head list; + unsigned int index; + char *name; +}; + +struct rproc_srm_regu_info { + struct list_head list; + unsigned int index; + struct regulator *regu; + const char *name; + bool enabled; +}; + +struct rproc_srm_dev { + struct device *dev; + struct pinctrl *pctrl; + + struct list_head clk_list_head; + struct list_head regu_list_head; + struct list_head pin_list_head; +}; + +/* Clocks */ +static void rproc_srm_dev_deconfig_clocks(struct rproc_srm_dev *rproc_srm_dev) +{ + struct rproc_srm_clk_info *c; + struct list_head *clk_head = &rproc_srm_dev->clk_list_head; + + list_for_each_entry(c, clk_head, list) { + if (!c->enabled) + continue; + + clk_disable_unprepare(c->clk); + c->enabled = false; + dev_dbg(rproc_srm_dev->dev, "clk %d (%s) deconfigured\n", + c->index, c->name); + } +} + +static int rproc_srm_dev_config_clocks(struct rproc_srm_dev *rproc_srm_dev) +{ + struct rproc_srm_clk_info *c; + struct list_head *clk_head = &rproc_srm_dev->clk_list_head; + int ret; + + /* Note: not only configuring, but also enabling */ + + list_for_each_entry(c, clk_head, list) { + if (c->enabled) + continue; + + ret = clk_prepare_enable(c->clk); + if (ret) { + dev_err(rproc_srm_dev->dev, "clk %d (%s) cfg failed\n", + c->index, c->name); + rproc_srm_dev_deconfig_clocks(rproc_srm_dev); + return ret; + } + c->enabled = true; + dev_dbg(rproc_srm_dev->dev, "clk %d (%s) configured\n", + c->index, c->name); + } + + return 0; +} + +static void rproc_srm_dev_put_clocks(struct rproc_srm_dev *rproc_srm_dev) +{ + struct device *dev = rproc_srm_dev->dev; + struct rproc_srm_clk_info *c, *tmp; + struct list_head *clk_head = &rproc_srm_dev->clk_list_head; + + list_for_each_entry_safe(c, tmp, clk_head, list) { + clk_put(c->clk); + dev_dbg(dev, "put clock %d (%s)\n", c->index, c->name); + list_del(&c->list); + } +} + +static int rproc_srm_dev_get_clocks(struct rproc_srm_dev *rproc_srm_dev) +{ + struct device *dev = rproc_srm_dev->dev; + struct device_node *np = dev->of_node; + struct rproc_srm_clk_info *c; + struct list_head *clk_head = &rproc_srm_dev->clk_list_head; + const char *name; + int nb_c, ret; + unsigned int i; + + if (!np) + return 0; + + nb_c = of_clk_get_parent_count(np); + if (!nb_c) + return 0; + + for (i = 0; i < nb_c; i++) { + c = devm_kzalloc(dev, sizeof(*c), GFP_KERNEL); + if (!c) { + ret = -ENOMEM; + goto err; + } + + c->clk = of_clk_get(np, i); + if (IS_ERR(c->clk)) { + dev_err(dev, "clock %d KO (%ld)\n", i, + PTR_ERR(c->clk)); + ret = -ENOMEM; + goto err; + } + + if (!of_property_read_string_index(np, "clock-names", i, + &name)) + c->name = devm_kstrdup(dev, name, GFP_KERNEL); + + c->index = i; + + list_add_tail(&c->list, clk_head); + dev_dbg(dev, "got clock %d (%s)\n", c->index, c->name); + } + + return 0; + +err: + rproc_srm_dev_put_clocks(rproc_srm_dev); + return ret; +} + +/* Regulators */ +static void rproc_srm_dev_deconfig_regus(struct rproc_srm_dev *rproc_srm_dev) +{ + struct rproc_srm_regu_info *r; + struct list_head *regu_head = &rproc_srm_dev->regu_list_head; + + list_for_each_entry(r, regu_head, list) { + if (!r->enabled) + continue; + + regulator_disable(r->regu); + r->enabled = false; + dev_dbg(rproc_srm_dev->dev, "regu %d (%s) disabled\n", + r->index, r->name); + } +} + +static int rproc_srm_dev_config_regus(struct rproc_srm_dev *rproc_srm_dev) +{ + struct rproc_srm_regu_info *r; + struct list_head *regu_head = &rproc_srm_dev->regu_list_head; + int ret; + + /* Enable all the regulators */ + list_for_each_entry(r, regu_head, list) { + if (r->enabled) + continue; + + ret = regulator_enable(r->regu); + if (ret) { + dev_err(rproc_srm_dev->dev, "regu %d (%s) failed\n", + r->index, r->name); + rproc_srm_dev_deconfig_regus(rproc_srm_dev); + return ret; + } + r->enabled = true; + dev_dbg(rproc_srm_dev->dev, "regu %d (%s) enabled\n", + r->index, r->name); + } + + return 0; +} + +static void rproc_srm_dev_put_regus(struct rproc_srm_dev *rproc_srm_dev) +{ + struct device *dev = rproc_srm_dev->dev; + struct rproc_srm_regu_info *r, *tmp; + struct list_head *regu_head = &rproc_srm_dev->regu_list_head; + + list_for_each_entry_safe(r, tmp, regu_head, list) { + devm_regulator_put(r->regu); + dev_dbg(dev, "put regu %d (%s)\n", r->index, r->name); + list_del(&r->list); + } +} + +static int rproc_srm_dev_get_regus(struct rproc_srm_dev *rproc_srm_dev) +{ + struct device *dev = rproc_srm_dev->dev; + struct device_node *np = dev->of_node; + struct property *p; + const char *n; + char *name; + struct rproc_srm_regu_info *r; + struct list_head *regu_head = &rproc_srm_dev->regu_list_head; + int ret, nb_s = 0; + + if (!np) + return 0; + + for_each_property_of_node(np, p) { + n = strstr(p->name, SUPPLY_SUFFIX); + if (!n || n == p->name) + continue; + + r = devm_kzalloc(dev, sizeof(*r), GFP_KERNEL); + if (!r) { + ret = -ENOMEM; + goto err_list; + } + + name = devm_kstrdup(dev, p->name, GFP_KERNEL); + name[strlen(p->name) - strlen(SUPPLY_SUFFIX)] = '\0'; + r->name = name; + + r->regu = devm_regulator_get(dev, r->name); + if (IS_ERR(r->regu)) { + dev_err(dev, "cannot get regu %s\n", r->name); + ret = -EINVAL; + goto err_list; + } + + r->index = nb_s++; + + list_add_tail(&r->list, regu_head); + dev_dbg(dev, "got regu %d (%s)\n", r->index, r->name); + } + + return 0; + +err_list: + rproc_srm_dev_put_regus(rproc_srm_dev); + return ret; +} + +/* Pins */ +static void rproc_srm_dev_put_pins(struct rproc_srm_dev *rproc_srm_dev) +{ + struct device *dev = rproc_srm_dev->dev; + struct rproc_srm_pin_info *p, *tmp; + struct list_head *pin_head = &rproc_srm_dev->pin_list_head; + + list_for_each_entry_safe(p, tmp, pin_head, list) { + devm_kfree(dev, p->name); + devm_kfree(dev, p); + dev_dbg(dev, "remove pin cfg %d (%s)\n", p->index, p->name); + list_del(&p->list); + } + + if (!IS_ERR_OR_NULL(rproc_srm_dev->pctrl)) + devm_pinctrl_put(rproc_srm_dev->pctrl); +} + +static int rproc_srm_dev_get_pins(struct rproc_srm_dev *rproc_srm_dev) +{ + struct device *dev = rproc_srm_dev->dev; + struct device_node *np = dev->of_node; + struct rproc_srm_pin_info *p; + struct list_head *pin_head = &rproc_srm_dev->pin_list_head; + int ret, nb_p; + unsigned int i; + const char *name; + + if (!np) + return 0; + + rproc_srm_dev->pctrl = devm_pinctrl_get(dev); + if (IS_ERR(rproc_srm_dev->pctrl)) + return 0; + + nb_p = of_property_count_strings(np, "pinctrl-names"); + if (!nb_p) { + dev_err(dev, "pinctrl-names not defined\n"); + devm_pinctrl_put(rproc_srm_dev->pctrl); + return -EINVAL; + } + + for (i = 0; i < nb_p; i++) { + p = devm_kzalloc(dev, sizeof(*p), GFP_KERNEL); + if (!p) { + ret = -ENOMEM; + goto err_list; + } + + if (of_property_read_string_index(np, "pinctrl-names", i, + &name)) { + dev_err(dev, "no pinctrl-names (pin %d)\n", i); + ret = -EINVAL; + goto err_list; + } + p->name = devm_kstrdup(dev, name, GFP_KERNEL); + + p->index = i; + + list_add_tail(&p->list, pin_head); + dev_dbg(dev, "found pin cfg %d (%s)\n", p->index, p->name); + } + return 0; + +err_list: + rproc_srm_dev_put_pins(rproc_srm_dev); + return ret; +} + +/* Core */ +static void +rproc_srm_dev_unbind(struct device *dev, struct device *master, void *data) +{ + struct rproc_srm_dev *rproc_srm_dev = dev_get_drvdata(dev); + + dev_dbg(dev, "%s\n", __func__); + + rproc_srm_dev_deconfig_regus(rproc_srm_dev); + rproc_srm_dev_deconfig_clocks(rproc_srm_dev); + + /* For pins: nothing to deconfig */ +} + +static int +rproc_srm_dev_bind(struct device *dev, struct device *master, void *data) +{ + struct rproc_srm_dev *rproc_srm_dev = dev_get_drvdata(dev); + int ret; + + dev_dbg(dev, "%s\n", __func__); + + ret = rproc_srm_dev_config_clocks(rproc_srm_dev); + if (ret) + return ret; + + ret = rproc_srm_dev_config_regus(rproc_srm_dev); + if (ret) + return ret; + + /* No need for pins config ("default" pinctrl applied before probe) */ + + return 0; +} + +static const struct component_ops rproc_srm_dev_ops = { + .bind = rproc_srm_dev_bind, + .unbind = rproc_srm_dev_unbind, +}; + +static int rproc_srm_dev_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rproc_srm_dev *rproc_srm_dev; + int ret; + + dev_dbg(dev, "%s for node %s\n", __func__, dev->of_node->name); + + rproc_srm_dev = devm_kzalloc(dev, sizeof(struct rproc_srm_dev), + GFP_KERNEL); + if (!rproc_srm_dev) + return -ENOMEM; + + rproc_srm_dev->dev = dev; + INIT_LIST_HEAD(&rproc_srm_dev->clk_list_head); + INIT_LIST_HEAD(&rproc_srm_dev->pin_list_head); + INIT_LIST_HEAD(&rproc_srm_dev->regu_list_head); + + /* Get clocks, regu and pinctrl */ + ret = rproc_srm_dev_get_clocks(rproc_srm_dev); + if (ret) + return ret; + + ret = rproc_srm_dev_get_regus(rproc_srm_dev); + if (ret) + goto err; + + ret = rproc_srm_dev_get_pins(rproc_srm_dev); + if (ret) + goto err; + + dev_set_drvdata(dev, rproc_srm_dev); + + return component_add(dev, &rproc_srm_dev_ops); + +err: + rproc_srm_dev_put_pins(rproc_srm_dev); + rproc_srm_dev_put_regus(rproc_srm_dev); + rproc_srm_dev_put_clocks(rproc_srm_dev); + return ret; +} + +static int rproc_srm_dev_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rproc_srm_dev *rproc_srm_dev = dev_get_drvdata(dev); + + dev_dbg(dev, "%s\n", __func__); + + component_del(dev, &rproc_srm_dev_ops); + + rproc_srm_dev_put_regus(rproc_srm_dev); + rproc_srm_dev_put_pins(rproc_srm_dev); + rproc_srm_dev_put_clocks(rproc_srm_dev); + + return 0; +} + +static const struct of_device_id rproc_srm_dev_match[] = { + { .compatible = "rproc-srm-dev", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, rproc_srm_dev_match); + +static struct platform_driver rproc_srm_dev_driver = { + .probe = rproc_srm_dev_probe, + .remove = rproc_srm_dev_remove, + .driver = { + .name = "rproc-srm-dev", + .of_match_table = of_match_ptr(rproc_srm_dev_match), + }, +}; + +module_platform_driver(rproc_srm_dev_driver); + +MODULE_AUTHOR("Fabien Dessenne "); +MODULE_DESCRIPTION("Remoteproc System Resource Manager driver - dev"); +MODULE_LICENSE("GPL v2");