From patchwork Wed Jun 27 08:06:20 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Fabien DESSENNE X-Patchwork-Id: 10490779 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 19D50602B3 for ; Wed, 27 Jun 2018 08:07:00 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 0B35C285F9 for ; Wed, 27 Jun 2018 08:07:00 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id F385A28A40; Wed, 27 Jun 2018 08:06:59 +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=-7.9 required=2.0 tests=BAYES_00, MAILING_LIST_MULTI, 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 C49D9285F9 for ; Wed, 27 Jun 2018 08:06:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S933089AbeF0IG6 (ORCPT ); Wed, 27 Jun 2018 04:06:58 -0400 Received: from mx08-00178001.pphosted.com ([91.207.212.93]:15253 "EHLO mx07-00178001.pphosted.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1753165AbeF0IGz (ORCPT ); Wed, 27 Jun 2018 04:06:55 -0400 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 w5R83cCf010350; Wed, 27 Jun 2018 10:06:52 +0200 Received: from beta.dmz-eu.st.com (beta.dmz-eu.st.com [164.129.1.35]) by mx08-00178001.pphosted.com with ESMTP id 2jv6gxr360-1 (version=TLSv1 cipher=ECDHE-RSA-AES256-SHA bits=256 verify=NOT); Wed, 27 Jun 2018 10:06:52 +0200 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 871AC56; Wed, 27 Jun 2018 08:06:51 +0000 (GMT) Received: from Webmail-eu.st.com (Safex1hubcas23.st.com [10.75.90.46]) by zeta.dmz-eu.st.com (STMicroelectronics) with ESMTP id 576FB12B3; Wed, 27 Jun 2018 08:06:51 +0000 (GMT) Received: from SAFEX1HUBCAS22.st.com (10.75.90.93) by SAFEX1HUBCAS23.st.com (10.75.90.46) with Microsoft SMTP Server (TLS) id 14.3.361.1; Wed, 27 Jun 2018 10:06:51 +0200 Received: from localhost (10.201.23.25) by Webmail-ga.st.com (10.75.90.48) with Microsoft SMTP Server (TLS) id 14.3.361.1; Wed, 27 Jun 2018 10:06:50 +0200 From: Fabien Dessenne To: Bjorn Andersson , , Arnaud Pouliquen , Loic Pallardy , "Oleksij Rempel" CC: Fabien Dessenne , Suman Anna , "Alex Elder" Subject: [RFC v2 3/5] remoteproc: add system resource manager device Date: Wed, 27 Jun 2018 10:06:20 +0200 Message-ID: <1530086782-5046-4-git-send-email-fabien.dessenne@st.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1530086782-5046-1-git-send-email-fabien.dessenne@st.com> References: <1530086782-5046-1-git-send-email-fabien.dessenne@st.com> MIME-Version: 1.0 X-Originating-IP: [10.201.23.25] X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10434:, , definitions=2018-06-27_02:, , 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 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 - interrupts which are prepared 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 | 570 +++++++++++++++++++++++++++++++++++++ 3 files changed, 581 insertions(+) create mode 100644 drivers/remoteproc/rproc_srm_dev.c diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index e981831..5d5cec9 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -21,6 +21,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 2be447a..7cb767b 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..4b2e6ac --- /dev/null +++ b/drivers/remoteproc/rproc_srm_dev.c @@ -0,0 +1,570 @@ +/* + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved + * Author: Fabien Dessenne . + * + * 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 +#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_irq_info { + struct list_head list; + unsigned int index; + char *name; + int irq; + 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; + struct list_head irq_list_head; +}; + +/* irqs */ +static void rproc_srm_dev_put_irqs(struct rproc_srm_dev *rproc_srm_dev) +{ + struct device *dev = rproc_srm_dev->dev; + struct rproc_srm_irq_info *info, *tmp; + struct list_head *irq_head = &rproc_srm_dev->irq_list_head; + + list_for_each_entry_safe(info, tmp, irq_head, list) { + devm_free_irq(dev, info->irq, NULL); + dev_dbg(dev, "Put irq %d (%s)\n", info->irq, info->name); + list_del(&info->list); + } +} + +static irqreturn_t rproc_srm_dev_irq_handler(int irq, void *dev) +{ + dev_warn(dev, "Spurious interrupt\n"); + return IRQ_HANDLED; +} + +static int rproc_srm_dev_get_irqs(struct rproc_srm_dev *rproc_srm_dev) +{ + struct device *dev = rproc_srm_dev->dev; + struct platform_device *pdev = to_platform_device(dev); + struct device_node *np = dev->of_node; + struct rproc_srm_irq_info *info; + struct list_head *irq_head = &rproc_srm_dev->irq_list_head; + const char *name; + int nr, ret, irq; + unsigned int i; + + if (!np) + return 0; + + nr = platform_irq_count(pdev); + dev_dbg(dev, "irq count = %d\n", nr); + if (!nr) + return 0; + + for (i = 0; i < nr; i++) { + info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); + if (!info) { + ret = -ENOMEM; + goto err; + } + + irq = platform_get_irq(pdev, i); + if (irq <= 0) { + ret = irq; + dev_err(dev, "Failed to get irq (%d)\n", ret); + goto err; + } + + info->irq = irq; + + /* Register a dummy irq handleras not used by Linux */ + ret = devm_request_irq(dev, info->irq, + rproc_srm_dev_irq_handler, 0, + dev_name(dev), NULL); + if (ret) { + dev_err(dev, "Failed to request irq (%d)\n", ret); + goto err; + } + + /* + * Disable IRQ. Since it is used by the remote processor we + * must not use the 'irq lazy disable' optimization + */ + irq_set_status_flags(info->irq, IRQ_DISABLE_UNLAZY); + + if (!of_property_read_string_index(np, "interrupt-names", i, + &name)) + info->name = devm_kstrdup(dev, name, GFP_KERNEL); + + info->index = i; + + list_add_tail(&info->list, irq_head); + dev_dbg(dev, "Got irq %d (%s)\n", info->irq, info->name); + } + + return 0; + +err: + rproc_srm_dev_put_irqs(rproc_srm_dev); + + return ret; +} + +/* 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); + rproc_srm_dev->pctrl = NULL; + } +} + +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; + + /* Assumption here is that "default" pinctrl applied before probe */ + + 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 <= 0) { + dev_err(dev, "pinctrl-names not defined\n"); + ret = -EINVAL; + goto err; + } + + for (i = 0; i < nb_p; i++) { + p = devm_kzalloc(dev, sizeof(*p), GFP_KERNEL); + if (!p) { + ret = -ENOMEM; + goto err; + } + + 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; + } + 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: + 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 and IRQs: nothing to deconfigure */ +} + +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; + + /* For pins and IRQs: nothing to configure */ + 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); + INIT_LIST_HEAD(&rproc_srm_dev->irq_list_head); + + /* Get clocks, regu, irqs 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; + + ret = rproc_srm_dev_get_irqs(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_irqs(rproc_srm_dev); + 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_irqs(rproc_srm_dev); + 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");