* Re: [PATCH v5 2/2] mmc: Add support for the ASPEED SD controller
From: Joel Stanley @ 2019-08-15 5:28 UTC (permalink / raw)
To: Andrew Jeffery
Cc: Mark Rutland, devicetree, ulf.hansson, linux-aspeed, Ryan Chen,
linux-mmc, adrian.hunter, Linux Kernel Mailing List, Rob Herring,
Linux ARM
In-Reply-To: <20190807003629.2974-3-andrew@aj.id.au>
On Wed, 7 Aug 2019 at 00:38, Andrew Jeffery <andrew@aj.id.au> wrote:
>
> Add a minimal driver for ASPEED's SD controller, which exposes two
> SDHCIs.
>
> The ASPEED design implements a common register set for the SDHCIs, and
> moves some of the standard configuration elements out to this common
> area (e.g. 8-bit mode, and card detect configuration which is not
> currently supported).
>
> The SD controller has a dedicated hardware interrupt that is shared
> between the slots. The common register set exposes information on which
> slot triggered the interrupt; early revisions of the patch introduced an
> irqchip for the register, but reality is it doesn't behave as an
> irqchip, and the result fits awkwardly into the irqchip APIs. Instead
> I've taken the simple approach of using the IRQ as a shared IRQ with
> some minor performance impact for the second slot.
>
> Ryan was the original author of the patch - I've taken his work and
> massaged it to drop the irqchip support and rework the devicetree
> integration. The driver has been smoke tested under qemu against a
> minimal SD controller model and lightly tested on an ast2500-evb.
>
> Signed-off-by: Ryan Chen <ryanchen.aspeed@gmail.com>
> Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
> Acked-by: Adrian Hunter <adrian.hunter@intel.com>
Reviewed-by: Joel Stanley <joel@jms.id.au>
>
> ---
>
> v5:
> * Cleanup sdhci driver on registration failure
>
> v4: No change
>
> v2:
> * Add AST2600 compatible
> * Drop SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN
> * Ensure slot number is valid
> * Fix build with CONFIG_MODULES
> * Fix module license string
> * Non-PCI devices won't die
> * Rename aspeed_sdc_configure_8bit_mode()
> * Rename aspeed_sdhci_pdata
> * Switch to sdhci_enable_clk()
> * Use PTR_ERR() on the right `struct platform_device *`
> ---
> drivers/mmc/host/Kconfig | 12 ++
> drivers/mmc/host/Makefile | 1 +
> drivers/mmc/host/sdhci-of-aspeed.c | 332 +++++++++++++++++++++++++++++
> 3 files changed, 345 insertions(+)
> create mode 100644 drivers/mmc/host/sdhci-of-aspeed.c
>
> diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
> index 14d89a108edd..0f8a230de2f3 100644
> --- a/drivers/mmc/host/Kconfig
> +++ b/drivers/mmc/host/Kconfig
> @@ -154,6 +154,18 @@ config MMC_SDHCI_OF_ARASAN
>
> If unsure, say N.
>
> +config MMC_SDHCI_OF_ASPEED
> + tristate "SDHCI OF support for the ASPEED SDHCI controller"
> + depends on MMC_SDHCI_PLTFM
> + depends on OF
> + help
> + This selects the ASPEED Secure Digital Host Controller Interface.
> +
> + If you have a controller with this interface, say Y or M here. You
> + also need to enable an appropriate bus interface.
> +
> + If unsure, say N.
> +
> config MMC_SDHCI_OF_AT91
> tristate "SDHCI OF support for the Atmel SDMMC controller"
> depends on MMC_SDHCI_PLTFM
> diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
> index 73578718f119..390ee162fe71 100644
> --- a/drivers/mmc/host/Makefile
> +++ b/drivers/mmc/host/Makefile
> @@ -84,6 +84,7 @@ obj-$(CONFIG_MMC_SDHCI_ESDHC_IMX) += sdhci-esdhc-imx.o
> obj-$(CONFIG_MMC_SDHCI_DOVE) += sdhci-dove.o
> obj-$(CONFIG_MMC_SDHCI_TEGRA) += sdhci-tegra.o
> obj-$(CONFIG_MMC_SDHCI_OF_ARASAN) += sdhci-of-arasan.o
> +obj-$(CONFIG_MMC_SDHCI_OF_ASPEED) += sdhci-of-aspeed.o
> obj-$(CONFIG_MMC_SDHCI_OF_AT91) += sdhci-of-at91.o
> obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o
> obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o
> diff --git a/drivers/mmc/host/sdhci-of-aspeed.c b/drivers/mmc/host/sdhci-of-aspeed.c
> new file mode 100644
> index 000000000000..8bb095ca2fa9
> --- /dev/null
> +++ b/drivers/mmc/host/sdhci-of-aspeed.c
> @@ -0,0 +1,332 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/* Copyright (C) 2019 ASPEED Technology Inc. */
> +/* Copyright (C) 2019 IBM Corp. */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/io.h>
> +#include <linux/mmc/host.h>
> +#include <linux/module.h>
> +#include <linux/of_address.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/spinlock.h>
> +
> +#include "sdhci-pltfm.h"
> +
> +#define ASPEED_SDC_INFO 0x00
> +#define ASPEED_SDC_S1MMC8 BIT(25)
> +#define ASPEED_SDC_S0MMC8 BIT(24)
> +
> +struct aspeed_sdc {
> + struct clk *clk;
> + struct resource *res;
> +
> + spinlock_t lock;
> + void __iomem *regs;
> +};
> +
> +struct aspeed_sdhci {
> + struct aspeed_sdc *parent;
> + u32 width_mask;
> +};
> +
> +static void aspeed_sdc_configure_8bit_mode(struct aspeed_sdc *sdc,
> + struct aspeed_sdhci *sdhci,
> + bool bus8)
> +{
> + u32 info;
> +
> + /* Set/clear 8 bit mode */
> + spin_lock(&sdc->lock);
> + info = readl(sdc->regs + ASPEED_SDC_INFO);
> + if (bus8)
> + info |= sdhci->width_mask;
> + else
> + info &= ~sdhci->width_mask;
> + writel(info, sdc->regs + ASPEED_SDC_INFO);
> + spin_unlock(&sdc->lock);
> +}
> +
> +static void aspeed_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
> +{
> + int div;
> + u16 clk;
> +
> + if (clock == host->clock)
> + return;
> +
> + sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
> +
> + if (clock == 0)
> + goto out;
> +
> + for (div = 1; div < 256; div *= 2) {
> + if ((host->max_clk / div) <= clock)
> + break;
> + }
> + div >>= 1;
> +
> + clk = div << SDHCI_DIVIDER_SHIFT;
> +
> + sdhci_enable_clk(host, clk);
> +
> +out:
> + host->clock = clock;
> +}
> +
> +static void aspeed_sdhci_set_bus_width(struct sdhci_host *host, int width)
> +{
> + struct sdhci_pltfm_host *pltfm_priv;
> + struct aspeed_sdhci *aspeed_sdhci;
> + struct aspeed_sdc *aspeed_sdc;
> + u8 ctrl;
> +
> + pltfm_priv = sdhci_priv(host);
> + aspeed_sdhci = sdhci_pltfm_priv(pltfm_priv);
> + aspeed_sdc = aspeed_sdhci->parent;
> +
> + /* Set/clear 8-bit mode */
> + aspeed_sdc_configure_8bit_mode(aspeed_sdc, aspeed_sdhci,
> + width == MMC_BUS_WIDTH_8);
> +
> + /* Set/clear 1 or 4 bit mode */
> + ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
> + if (width == MMC_BUS_WIDTH_4)
> + ctrl |= SDHCI_CTRL_4BITBUS;
> + else
> + ctrl &= ~SDHCI_CTRL_4BITBUS;
> + sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
> +}
> +
> +static const struct sdhci_ops aspeed_sdhci_ops = {
> + .set_clock = aspeed_sdhci_set_clock,
> + .get_max_clock = sdhci_pltfm_clk_get_max_clock,
> + .set_bus_width = aspeed_sdhci_set_bus_width,
> + .get_timeout_clock = sdhci_pltfm_clk_get_max_clock,
> + .reset = sdhci_reset,
> + .set_uhs_signaling = sdhci_set_uhs_signaling,
> +};
> +
> +static const struct sdhci_pltfm_data aspeed_sdhci_pdata = {
> + .ops = &aspeed_sdhci_ops,
> + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
> +};
> +
> +static inline int aspeed_sdhci_calculate_slot(struct aspeed_sdhci *dev,
> + struct resource *res)
> +{
> + resource_size_t delta;
> +
> + if (!res || resource_type(res) != IORESOURCE_MEM)
> + return -EINVAL;
> +
> + if (res->start < dev->parent->res->start)
> + return -EINVAL;
> +
> + delta = res->start - dev->parent->res->start;
> + if (delta & (0x100 - 1))
> + return -EINVAL;
> +
> + return (delta / 0x100) - 1;
> +}
> +
> +static int aspeed_sdhci_probe(struct platform_device *pdev)
> +{
> + struct sdhci_pltfm_host *pltfm_host;
> + struct aspeed_sdhci *dev;
> + struct sdhci_host *host;
> + struct resource *res;
> + int slot;
> + int ret;
> +
> + host = sdhci_pltfm_init(pdev, &aspeed_sdhci_pdata, sizeof(*dev));
> + if (IS_ERR(host))
> + return PTR_ERR(host);
> +
> + pltfm_host = sdhci_priv(host);
> + dev = sdhci_pltfm_priv(pltfm_host);
> + dev->parent = dev_get_drvdata(pdev->dev.parent);
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + slot = aspeed_sdhci_calculate_slot(dev, res);
> +
> + if (slot < 0)
> + return slot;
> + else if (slot >= 2)
> + return -EINVAL;
> +
> + dev_info(&pdev->dev, "Configuring for slot %d\n", slot);
> + dev->width_mask = !slot ? ASPEED_SDC_S0MMC8 : ASPEED_SDC_S1MMC8;
> +
> + sdhci_get_of_property(pdev);
> +
> + pltfm_host->clk = devm_clk_get(&pdev->dev, NULL);
> + if (IS_ERR(pltfm_host->clk))
> + return PTR_ERR(pltfm_host->clk);
> +
> + ret = clk_prepare_enable(pltfm_host->clk);
> + if (ret) {
> + dev_err(&pdev->dev, "Unable to enable SDIO clock\n");
> + goto err_pltfm_free;
> + }
> +
> + ret = mmc_of_parse(host->mmc);
> + if (ret)
> + goto err_sdhci_add;
> +
> + ret = sdhci_add_host(host);
> + if (ret)
> + goto err_sdhci_add;
> +
> + return 0;
> +
> +err_sdhci_add:
> + clk_disable_unprepare(pltfm_host->clk);
> +err_pltfm_free:
> + sdhci_pltfm_free(pdev);
> + return ret;
> +}
> +
> +static int aspeed_sdhci_remove(struct platform_device *pdev)
> +{
> + struct sdhci_pltfm_host *pltfm_host;
> + struct sdhci_host *host;
> + int dead = 0;
> +
> + host = platform_get_drvdata(pdev);
> + pltfm_host = sdhci_priv(host);
> +
> + sdhci_remove_host(host, dead);
> +
> + clk_disable_unprepare(pltfm_host->clk);
> +
> + sdhci_pltfm_free(pdev);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id aspeed_sdhci_of_match[] = {
> + { .compatible = "aspeed,ast2400-sdhci", },
> + { .compatible = "aspeed,ast2500-sdhci", },
> + { .compatible = "aspeed,ast2600-sdhci", },
> + { }
> +};
> +
> +static struct platform_driver aspeed_sdhci_driver = {
> + .driver = {
> + .name = "sdhci-aspeed",
> + .of_match_table = aspeed_sdhci_of_match,
> + },
> + .probe = aspeed_sdhci_probe,
> + .remove = aspeed_sdhci_remove,
> +};
> +
> +static int aspeed_sdc_probe(struct platform_device *pdev)
> +
> +{
> + struct device_node *parent, *child;
> + struct aspeed_sdc *sdc;
> + int ret;
> +
> + sdc = devm_kzalloc(&pdev->dev, sizeof(*sdc), GFP_KERNEL);
> + if (!sdc)
> + return -ENOMEM;
> +
> + spin_lock_init(&sdc->lock);
> +
> + sdc->clk = devm_clk_get(&pdev->dev, NULL);
> + if (IS_ERR(sdc->clk))
> + return PTR_ERR(sdc->clk);
> +
> + ret = clk_prepare_enable(sdc->clk);
> + if (ret) {
> + dev_err(&pdev->dev, "Unable to enable SDCLK\n");
> + return ret;
> + }
> +
> + sdc->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + sdc->regs = devm_ioremap_resource(&pdev->dev, sdc->res);
> + if (IS_ERR(sdc->regs)) {
> + ret = PTR_ERR(sdc->regs);
> + goto err_clk;
> + }
> +
> + dev_set_drvdata(&pdev->dev, sdc);
> +
> + parent = pdev->dev.of_node;
> + for_each_available_child_of_node(parent, child) {
> + struct platform_device *cpdev;
> +
> + cpdev = of_platform_device_create(child, NULL, &pdev->dev);
> + if (IS_ERR(cpdev)) {
> + of_node_put(child);
> + ret = PTR_ERR(cpdev);
> + goto err_clk;
> + }
> + }
> +
> + return 0;
> +
> +err_clk:
> + clk_disable_unprepare(sdc->clk);
> + return ret;
> +}
> +
> +static int aspeed_sdc_remove(struct platform_device *pdev)
> +{
> + struct aspeed_sdc *sdc = dev_get_drvdata(&pdev->dev);
> +
> + clk_disable_unprepare(sdc->clk);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id aspeed_sdc_of_match[] = {
> + { .compatible = "aspeed,ast2400-sd-controller", },
> + { .compatible = "aspeed,ast2500-sd-controller", },
> + { .compatible = "aspeed,ast2600-sd-controller", },
> + { }
> +};
> +
> +MODULE_DEVICE_TABLE(of, aspeed_sdc_of_match);
> +
> +static struct platform_driver aspeed_sdc_driver = {
> + .driver = {
> + .name = "sd-controller-aspeed",
> + .pm = &sdhci_pltfm_pmops,
> + .of_match_table = aspeed_sdc_of_match,
> + },
> + .probe = aspeed_sdc_probe,
> + .remove = aspeed_sdc_remove,
> +};
> +
> +static int __init aspeed_sdc_init(void)
> +{
> + int rc;
> +
> + rc = platform_driver_register(&aspeed_sdhci_driver);
> + if (rc < 0)
> + return rc;
> +
> + rc = platform_driver_register(&aspeed_sdc_driver);
> + if (rc < 0)
> + platform_driver_unregister(&aspeed_sdhci_driver);
> +
> + return rc;
> +}
> +module_init(aspeed_sdc_init);
> +
> +static void __exit aspeed_sdc_exit(void)
> +{
> + platform_driver_unregister(&aspeed_sdc_driver);
> + platform_driver_unregister(&aspeed_sdhci_driver);
> +}
> +module_exit(aspeed_sdc_exit);
> +
> +MODULE_DESCRIPTION("Driver for the ASPEED SD/SDIO/SDHCI Controllers");
> +MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>");
> +MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>");
> +MODULE_LICENSE("GPL");
> --
> 2.20.1
>
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v5 1/2] dt-bindings: mmc: Document Aspeed SD controller
From: Joel Stanley @ 2019-08-15 5:36 UTC (permalink / raw)
To: Andrew Jeffery
Cc: Mark Rutland, devicetree, ulf.hansson, linux-aspeed, Ryan Chen,
linux-mmc, adrian.hunter, Linux Kernel Mailing List, Rob Herring,
Linux ARM
In-Reply-To: <20190807003629.2974-2-andrew@aj.id.au>
On Wed, 7 Aug 2019 at 00:38, Andrew Jeffery <andrew@aj.id.au> wrote:
>
> The ASPEED SD/SDIO/MMC controller exposes two slots implementing the
> SDIO Host Specification v2.00, with 1 or 4 bit data buses, or an 8 bit
> data bus if only a single slot is enabled.
>
> Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
Reviewed-by: Joel Stanley <joel@jms.id.au>
Two minor comments below.
> +++ b/Documentation/devicetree/bindings/mmc/aspeed,sdhci.yaml
> @@ -0,0 +1,105 @@
> +# SPDX-License-Identifier: GPL-2.0-or-later
No "Copyright IBM" ?
> +%YAML 1.2
> +---
> +
> +examples:
> + - |
> + #include <dt-bindings/clock/aspeed-clock.h>
> + sdc@1e740000 {
> + compatible = "aspeed,ast2500-sd-controller";
> + reg = <0x1e740000 0x100>;
> + #address-cells = <1>;
> + #size-cells = <1>;
> + ranges = <0 0x1e740000 0x10000>;
According to the datasheet this could be 0x20000. It does not matter
though, as there's nothing in it past 0x300.
Cheers,
Joel
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v5 1/2] dt-bindings: mmc: Document Aspeed SD controller
From: Andrew Jeffery @ 2019-08-15 5:38 UTC (permalink / raw)
To: Joel Stanley
Cc: Mark Rutland, devicetree, Ulf Hansson, linux-aspeed, Ryan Chen,
linux-mmc, Adrian Hunter, Linux Kernel Mailing List, Rob Herring,
Linux ARM
In-Reply-To: <CACPK8Xe6Zp1uOqEffEc0b6oGa7portEAifGPRqb876HmA+oZeg@mail.gmail.com>
On Thu, 15 Aug 2019, at 15:06, Joel Stanley wrote:
> On Wed, 7 Aug 2019 at 00:38, Andrew Jeffery <andrew@aj.id.au> wrote:
> >
> > The ASPEED SD/SDIO/MMC controller exposes two slots implementing the
> > SDIO Host Specification v2.00, with 1 or 4 bit data buses, or an 8 bit
> > data bus if only a single slot is enabled.
> >
> > Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
>
> Reviewed-by: Joel Stanley <joel@jms.id.au>
>
> Two minor comments below.
>
> > +++ b/Documentation/devicetree/bindings/mmc/aspeed,sdhci.yaml
> > @@ -0,0 +1,105 @@
> > +# SPDX-License-Identifier: GPL-2.0-or-later
>
> No "Copyright IBM" ?
I'm going rogue.
That reminds me I should chase up where we got to with the binding
licensing.
>
> > +%YAML 1.2
> > +---
>
> > +
> > +examples:
> > + - |
> > + #include <dt-bindings/clock/aspeed-clock.h>
> > + sdc@1e740000 {
> > + compatible = "aspeed,ast2500-sd-controller";
> > + reg = <0x1e740000 0x100>;
> > + #address-cells = <1>;
> > + #size-cells = <1>;
> > + ranges = <0 0x1e740000 0x10000>;
>
> According to the datasheet this could be 0x20000. It does not matter
> though, as there's nothing in it past 0x300.
Good catch.
Andrew
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [PATCH v2 1/2] iommu/arm-smmu-v3: don't add a master into smmu_domain before it's ready
From: Zhen Lei @ 2019-08-15 5:44 UTC (permalink / raw)
To: Jean-Philippe Brucker, Jean-Philippe Brucker, John Garry,
Robin Murphy, Will Deacon, Joerg Roedel, iommu, linux-arm-kernel,
linux-kernel
Cc: Zhen Lei
In-Reply-To: <20190815054439.30652-1-thunder.leizhen@huawei.com>
Once a master has been added into smmu_domain->devices, it may immediately
be scaned in arm_smmu_unmap()-->arm_smmu_atc_inv_domain(). From a logical
point of view, the master should be added into smmu_domain after it has
been completely initialized.
Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>
---
drivers/iommu/arm-smmu-v3.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index a9a9fabd396804a..29056d9bb12aa01 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -1958,10 +1958,6 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
master->domain = smmu_domain;
- spin_lock_irqsave(&smmu_domain->devices_lock, flags);
- list_add(&master->domain_head, &smmu_domain->devices);
- spin_unlock_irqrestore(&smmu_domain->devices_lock, flags);
-
if (smmu_domain->stage != ARM_SMMU_DOMAIN_BYPASS)
arm_smmu_enable_ats(master);
@@ -1969,6 +1965,10 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
arm_smmu_write_ctx_desc(smmu, &smmu_domain->s1_cfg);
arm_smmu_install_ste_for_dev(master);
+
+ spin_lock_irqsave(&smmu_domain->devices_lock, flags);
+ list_add(&master->domain_head, &smmu_domain->devices);
+ spin_unlock_irqrestore(&smmu_domain->devices_lock, flags);
out_unlock:
mutex_unlock(&smmu_domain->init_mutex);
return ret;
--
1.8.3
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v2 2/2] iommu/arm-smmu-v3: add nr_ats_masters for quickly check
From: Zhen Lei @ 2019-08-15 5:44 UTC (permalink / raw)
To: Jean-Philippe Brucker, Jean-Philippe Brucker, John Garry,
Robin Murphy, Will Deacon, Joerg Roedel, iommu, linux-arm-kernel,
linux-kernel
Cc: Zhen Lei
In-Reply-To: <20190815054439.30652-1-thunder.leizhen@huawei.com>
When (smmu_domain->smmu->features & ARM_SMMU_FEAT_ATS) is true, even if a
smmu domain does not contain any ats master, the operations of
arm_smmu_atc_inv_to_cmd() and lock protection in arm_smmu_atc_inv_domain()
are always executed. This will impact performance, especially in
multi-core and stress scenarios. For my FIO test scenario, about 8%
performance reduced.
In fact, we can use a struct member to record how many ats masters that
the smmu contains. And check that without traverse the list and check all
masters one by one in the lock protection.
Fixes: 9ce27afc0830 ("iommu/arm-smmu-v3: Add support for PCI ATS")
Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>
---
drivers/iommu/arm-smmu-v3.c | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index 29056d9bb12aa01..154334d3310c9b8 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -631,6 +631,7 @@ struct arm_smmu_domain {
struct io_pgtable_ops *pgtbl_ops;
bool non_strict;
+ int nr_ats_masters;
enum arm_smmu_domain_stage stage;
union {
@@ -1531,7 +1532,16 @@ static int arm_smmu_atc_inv_domain(struct arm_smmu_domain *smmu_domain,
struct arm_smmu_cmdq_ent cmd;
struct arm_smmu_master *master;
- if (!(smmu_domain->smmu->features & ARM_SMMU_FEAT_ATS))
+ /*
+ * The protectiom of spinlock(&iommu_domain->devices_lock) is omitted.
+ * Because for a given master, its map/unmap operations should only be
+ * happened after it has been attached and before it has been detached.
+ * So that, if at least one master need to be atc invalidated, the
+ * value of smmu_domain->nr_ats_masters can not be zero.
+ *
+ * This can alleviate performance loss in multi-core scenarios.
+ */
+ if (!smmu_domain->nr_ats_masters)
return 0;
arm_smmu_atc_inv_to_cmd(ssid, iova, size, &cmd);
@@ -1913,6 +1923,7 @@ static void arm_smmu_detach_dev(struct arm_smmu_master *master)
spin_lock_irqsave(&smmu_domain->devices_lock, flags);
list_del(&master->domain_head);
+ smmu_domain->nr_ats_masters--;
spin_unlock_irqrestore(&smmu_domain->devices_lock, flags);
master->domain = NULL;
@@ -1968,6 +1979,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
spin_lock_irqsave(&smmu_domain->devices_lock, flags);
list_add(&master->domain_head, &smmu_domain->devices);
+ smmu_domain->nr_ats_masters++;
spin_unlock_irqrestore(&smmu_domain->devices_lock, flags);
out_unlock:
mutex_unlock(&smmu_domain->init_mutex);
--
1.8.3
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v2 0/2] add nr_ats_masters for quickly check
From: Zhen Lei @ 2019-08-15 5:44 UTC (permalink / raw)
To: Jean-Philippe Brucker, Jean-Philippe Brucker, John Garry,
Robin Murphy, Will Deacon, Joerg Roedel, iommu, linux-arm-kernel,
linux-kernel
Cc: Zhen Lei
v1 --> v2:
1. change the type of nr_ats_masters from atomic_t to int, and move its
ind/dec operation from arm_smmu_enable_ats()/arm_smmu_disable_ats() to
arm_smmu_attach_dev()/arm_smmu_detach_dev(), and protected by
"spin_lock_irqsave(&smmu_domain->devices_lock, flags);"
Zhen Lei (2):
iommu/arm-smmu-v3: don't add a master into smmu_domain before it's
ready
iommu/arm-smmu-v3: add nr_ats_masters for quickly check
drivers/iommu/arm-smmu-v3.c | 22 +++++++++++++++++-----
1 file changed, 17 insertions(+), 5 deletions(-)
--
1.8.3
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH] iommu/arm-smmu-v3: add nr_ats_masters to avoid unnecessary operations
From: Leizhen (ThunderTown) @ 2019-08-15 5:46 UTC (permalink / raw)
To: Will Deacon
Cc: Jean-Philippe Brucker, Joerg Roedel, John Garry, linux-kernel,
iommu, Robin Murphy, linux-arm-kernel
In-Reply-To: <20190814111402.pxlvtmv44nhuvhio@willie-the-truck>
On 2019/8/14 19:14, Will Deacon wrote:
> Hi,
>
> I've been struggling with the memory ordering requirements here. More below.
>
> On Thu, Aug 01, 2019 at 08:20:40PM +0800, Zhen Lei wrote:
>> When (smmu_domain->smmu->features & ARM_SMMU_FEAT_ATS) is true, even if a
>> smmu domain does not contain any ats master, the operations of
>> arm_smmu_atc_inv_to_cmd() and lock protection in arm_smmu_atc_inv_domain()
>> are always executed. This will impact performance, especially in
>> multi-core and stress scenarios. For my FIO test scenario, about 8%
>> performance reduced.
>>
>> In fact, we can use a atomic member to record how many ats masters the
>> smmu contains. And check that without traverse the list and check all
>> masters one by one in the lock protection.
>>
>> Fixes: 9ce27afc0830 ("iommu/arm-smmu-v3: Add support for PCI ATS")
>> Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>
>> ---
>> drivers/iommu/arm-smmu-v3.c | 10 ++++++++--
>> 1 file changed, 8 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
>> index a9a9fabd396804a..1b370d9aca95f94 100644
>> --- a/drivers/iommu/arm-smmu-v3.c
>> +++ b/drivers/iommu/arm-smmu-v3.c
>> @@ -631,6 +631,7 @@ struct arm_smmu_domain {
>>
>> struct io_pgtable_ops *pgtbl_ops;
>> bool non_strict;
>> + atomic_t nr_ats_masters;
>>
>> enum arm_smmu_domain_stage stage;
>> union {
>> @@ -1531,7 +1532,7 @@ static int arm_smmu_atc_inv_domain(struct arm_smmu_domain *smmu_domain,
>> struct arm_smmu_cmdq_ent cmd;
>> struct arm_smmu_master *master;
>>
>> - if (!(smmu_domain->smmu->features & ARM_SMMU_FEAT_ATS))
>> + if (!atomic_read(&smmu_domain->nr_ats_masters))
>> return 0;
>
> This feels wrong to me: the CPU can speculate ahead of time that
> 'nr_ats_masters' is 0, but we could have a concurrent call to '->attach()'
> for an ATS-enabled device. Wouldn't it then be possible for the new device
> to populate its ATC as a result of speculative accesses for the mapping that
> we're tearing down?
>
> The devices lock solves this problem by serialising invalidation with
> '->attach()/->detach()' operations.
>
> John's suggestion of RCU might work better, but I think you'll need to call
> synchronize_rcu() between adding yourself to the 'devices' list and enabling
> ATS.
>
> What do you think?
I have updated my patch and just sent, below it's my thoughts.
- if (!(smmu_domain->smmu->features & ARM_SMMU_FEAT_ATS))
+ /*
+ * The protectiom of spinlock(&iommu_domain->devices_lock) is omitted.
+ * Because for a given master, its map/unmap operations should only be
+ * happened after it has been attached and before it has been detached.
+ * So that, if at least one master need to be atc invalidated, the
+ * value of smmu_domain->nr_ats_masters can not be zero.
+ *
+ * This can alleviate performance loss in multi-core scenarios.
+ */
+ if (!smmu_domain->nr_ats_masters)
>
>> arm_smmu_atc_inv_to_cmd(ssid, iova, size, &cmd);
>> @@ -1869,6 +1870,7 @@ static int arm_smmu_enable_ats(struct arm_smmu_master *master)
>> size_t stu;
>> struct pci_dev *pdev;
>> struct arm_smmu_device *smmu = master->smmu;
>> + struct arm_smmu_domain *smmu_domain = master->domain;
>> struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(master->dev);
>>
>> if (!(smmu->features & ARM_SMMU_FEAT_ATS) || !dev_is_pci(master->dev) ||
>> @@ -1887,12 +1889,15 @@ static int arm_smmu_enable_ats(struct arm_smmu_master *master)
>> return ret;
>>
>> master->ats_enabled = true;
>> + atomic_inc(&smmu_domain->nr_ats_masters);
>
> Here, we need to make sure that concurrent invalidation sees the updated
> 'nr_ats_masters' value before ATS is enabled for the device, otherwise we
> could miss an ATC invalidation.
>
> I think the code above gets this guarantee because of the way that ATS is
> enabled in the STE, which ensures that we issue invalidation commands before
> making the STE 'live'; this has the side-effect of a write barrier before
> updating PROD, which I think we also rely on for installing the CD pointer.
>
> Put another way: writes are ordered before a subsequent command insertion.
>
> Do you agree? If so, I'll add a comment because this is subtle and easily
> overlooked.
>
>> static void arm_smmu_disable_ats(struct arm_smmu_master *master)
>> {
>> struct arm_smmu_cmdq_ent cmd;
>> + struct arm_smmu_domain *smmu_domain = master->domain;
>>
>> if (!master->ats_enabled || !dev_is_pci(master->dev))
>> return;
>> @@ -1901,6 +1906,7 @@ static void arm_smmu_disable_ats(struct arm_smmu_master *master)
>> arm_smmu_atc_inv_master(master, &cmd);
>> pci_disable_ats(to_pci_dev(master->dev));
>> master->ats_enabled = false;
>> + atomic_dec(&smmu_domain->nr_ats_masters);
>
> This part is the other way around: now we need to ensure that we don't
> decrement 'nr_ats_masters' until we've disabled ATS. This works for a
> number of reasons, none of which are obvious:
>
> - The control dependency from completing the prior CMD_SYNCs for tearing
> down the STE and invalidating the ATC
>
> - The spinlock handover from the CMD_SYNCs above
>
> - The writel() when poking PCI configuration space in pci_disable_ats()
> happens to be implemented with a write-write barrier
>
> I suppose the control dependency is the most compelling one: we can't let
> stores out whilst we're awaiting completion of a CMD_SYNC.
>
> Put another way: writes are ordered after the completion of a prior CMD_SYNC.
>
> But yeah, I need to write this down.
>
>> static void arm_smmu_detach_dev(struct arm_smmu_master *master)
>> @@ -1915,10 +1921,10 @@ static void arm_smmu_detach_dev(struct arm_smmu_master *master)
>> list_del(&master->domain_head);
>> spin_unlock_irqrestore(&smmu_domain->devices_lock, flags);
>>
>> - master->domain = NULL;
>> arm_smmu_install_ste_for_dev(master);
>>
>> arm_smmu_disable_ats(master);
>> + master->domain = NULL;
>
> As you mentioned, this is broken. Can you simply drop this hunk completely?
>
> Will
>
> .
>
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [PATCH] i2c: iproc: Make bcm_iproc_i2c_quirks constant
From: Nishka Dasgupta @ 2019-08-15 5:55 UTC (permalink / raw)
To: rjui, sbranden, bcm-kernel-feedback-list, linux-i2c,
linux-arm-kernel
Cc: Nishka Dasgupta
Static structure bcm_iproc_i2c_quirks, of type i2c_adapter_quirks, is
only used when being assigned to constant field quirks of a variable
having type i2c_adapter. Hence make bcm_iproc_i2c_quirks constant as
well to prevent it from unintended modification.
Issue found with Coccinelle.
Signed-off-by: Nishka Dasgupta <nishkadg.linux@gmail.com>
---
drivers/i2c/busses/i2c-bcm-iproc.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c
index d7fd76baec92..e9f0e5b6eadc 100644
--- a/drivers/i2c/busses/i2c-bcm-iproc.c
+++ b/drivers/i2c/busses/i2c-bcm-iproc.c
@@ -805,7 +805,7 @@ static struct i2c_algorithm bcm_iproc_algo = {
.unreg_slave = bcm_iproc_i2c_unreg_slave,
};
-static struct i2c_adapter_quirks bcm_iproc_i2c_quirks = {
+static const struct i2c_adapter_quirks bcm_iproc_i2c_quirks = {
.max_read_len = M_RX_MAX_READ_LEN,
};
--
2.19.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH] i2c: stm32f7: Make structure stm32f7_i2c_algo constant
From: Nishka Dasgupta @ 2019-08-15 5:58 UTC (permalink / raw)
To: pierre-yves.mordret, mcoquelin.stm32, alexandre.torgue, linux-i2c,
linux-stm32, linux-arm-kernel, linux-kernel
Cc: Nishka Dasgupta
Static structure stm32f7_i2c_algo, of type i2c_algorithm, is used only
when it is assigned to constant field algo of a variable having type
i2c_adapter. As stm32f7_i2c_algo is therefore never modified, make it
const as well to protect it from unintended modification.
Issue found with Coccinelle.
Signed-off-by: Nishka Dasgupta <nishkadg.linux@gmail.com>
---
drivers/i2c/busses/i2c-stm32f7.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/i2c/busses/i2c-stm32f7.c b/drivers/i2c/busses/i2c-stm32f7.c
index 266d1c269b83..d36cf08461f7 100644
--- a/drivers/i2c/busses/i2c-stm32f7.c
+++ b/drivers/i2c/busses/i2c-stm32f7.c
@@ -1809,7 +1809,7 @@ static u32 stm32f7_i2c_func(struct i2c_adapter *adap)
I2C_FUNC_SMBUS_I2C_BLOCK;
}
-static struct i2c_algorithm stm32f7_i2c_algo = {
+static const struct i2c_algorithm stm32f7_i2c_algo = {
.master_xfer = stm32f7_i2c_xfer,
.smbus_xfer = stm32f7_i2c_smbus_xfer,
.functionality = stm32f7_i2c_func,
--
2.19.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* Re: kexec on rk3399
From: Felipe Balbi @ 2019-08-15 6:00 UTC (permalink / raw)
To: Vicente Bergas
Cc: Matthias Brugger, Heiko Stuebner, Marc Zyngier, Catalin Marinas,
linux-usb, Will Deacon, linux-kernel, linux-rockchip,
Greg Kroah-Hartman, Robin Murphy, linux-arm-kernel
In-Reply-To: <4fc3e5b5-31fe-41f6-8031-b37454f21437@gmail.com>
Hi,
Vicente Bergas <vicencb@gmail.com> writes:
>> Vicente Bergas <vicencb@gmail.com> writes:
>>> On Monday, July 22, 2019 4:31:27 PM CEST, Vicente Bergas wrote:
>>>> Hi, i have been running linux on rk3399 booted with kexec fine until 5.2
>>>> From 5.2 onwards, there are memory corruption issues as reported here:
>>>> http://lkml.iu.edu/hypermail/linux/kernel/1906.2/07211.html
>>>> kexec has been identified as the principal reason for the issues.
>>>>
>>>> It turns out that kexec has never worked reliably on this platform, ...
>>>
>>> Thank you all for your suggestions on where the issue could be.
>>>
>>> It seems that it was the USB driver.
>>> Now using v5.2.8 booted with kexec from v5.2.8 with a workaround and
>>> so far so good. It is being tested on the Sapphire board.
>>>
>>> The workaround is:
>>> --- a/drivers/usb/dwc3/dwc3-of-simple.c
>>> +++ b/drivers/usb/dwc3/dwc3-of-simple.c
>>> @@ -133,6 +133,13 @@
>>> return 0;
>>> }
>>>
>>> +static void dwc3_of_simple_shutdown(struct platform_device *pdev)
>>> +{
>>> + struct dwc3_of_simple *simple = platform_get_drvdata(pdev);
>>> +
>>> + reset_control_assert(simple->resets);
>>> +}
>>> +
>>> static int __maybe_unused dwc3_of_simple_runtime_suspend(struct device
>>> *dev)
>>> {
>>> struct dwc3_of_simple *simple = dev_get_drvdata(dev);
>>> @@ -190,6 +197,7 @@
>>> static struct platform_driver dwc3_of_simple_driver = {
>>> .probe = dwc3_of_simple_probe,
>>> .remove = dwc3_of_simple_remove,
>>> + .shutdown = dwc3_of_simple_shutdown,
>>> .driver = {
>>> .name = "dwc3-of-simple",
>>> .of_match_table = of_dwc3_simple_match,
>>>
>>> If this patch is OK after review i can resubmit it as a pull request.
>>
>> not a pull request, just send a patch using git send-email
>>
>>> Should a similar change be applied to drivers/usb/dwc3/core.c ?
>>
>> Is it necessary? We haven't had any bug reports regarding that. Also, if
>> we have reset control support in the core driver, why do we need it in
>> of_simple? Seems like of_simple could just rely on what core does.
>
> the workaround has been tested patching only core.c with
> --- a/drivers/usb/dwc3/core.c
> +++ b/drivers/usb/dwc3/core.c
> @@ -1561,6 +1561,13 @@
> return 0;
> }
>
> +static void dwc3_shutdown(struct platform_device *pdev)
> +{
> + struct dwc3 *dwc = platform_get_drvdata(pdev);
> +
> + reset_control_assert(dwc->reset);
> +}
> +
> #ifdef CONFIG_PM
> static int dwc3_core_init_for_resume(struct dwc3 *dwc)
> {
> @@ -1866,6 +1873,7 @@
> static struct platform_driver dwc3_driver = {
> .probe = dwc3_probe,
> .remove = dwc3_remove,
> + .shutdown = dwc3_shutdown,
> .driver = {
> .name = "dwc3",
> .of_match_table = of_match_ptr(of_dwc3_match),
>
> and leaving dwc3-of-simple.c as is, the issue persisted.
That's because your reset controller is not passed to dwc3 core, only to
your glue layer.
--
balbi
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: kexec on rk3399
From: Felipe Balbi @ 2019-08-15 6:06 UTC (permalink / raw)
To: Vicente Bergas, Robin Murphy
Cc: Matthias Brugger, Heiko Stuebner, Marc Zyngier, Catalin Marinas,
linux-usb, Will Deacon, linux-kernel, linux-rockchip,
Greg Kroah-Hartman, linux-arm-kernel
In-Reply-To: <59055782-7fc2-4b16-af8b-a56fb845a43f@gmail.com>
Hi,
Vicente Bergas <vicencb@gmail.com> writes:
> On Wednesday, August 14, 2019 3:12:26 PM CEST, Robin Murphy wrote:
>> On 14/08/2019 13:53, Vicente Bergas wrote:
>>> On Monday, July 22, 2019 4:31:27 PM CEST, Vicente Bergas wrote: ...
>>
>> This particular change looks like it's implicitly specific to
>> RK3399, which wouldn't be ideal. Presumably if the core dwc3
>> driver implemented shutdown correctly (echoing parts of
>> dwc3_remove(), I guess) then the glue layers shouldn't need
>> anything special anyway.
>>
>> Robin.
>
> I just checked simple->resets from dwc3-of-simple.c and it is an array
> with multiple resets whereas dwc->reset from core.c is NULL.
> So the reset seems specific to the glue layers.
> Is there another way than resetting the thing that is
> generic enough to go to core.c and allows kexec?
This is a really odd 'failure'. We do full soft reset during driver
initialization on dwc3. We shouldn't need to assert reset on shutdown,
really.
I think the problem is here:
if (simple->pulse_resets) {
ret = reset_control_reset(simple->resets);
if (ret)
goto err_resetc_put;
} else {
ret = reset_control_deassert(simple->resets);
if (ret)
goto err_resetc_put;
}
Note that if pulse_resets is set, we will run a reset. But if
pulse_resets is false and need_reset is true, we deassert the reset.
I think below patch is enough:
diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c
index bdac3e7d7b18..9a2f3e09aa2e 100644
--- a/drivers/usb/dwc3/dwc3-of-simple.c
+++ b/drivers/usb/dwc3/dwc3-of-simple.c
@@ -72,7 +72,15 @@ static int dwc3_of_simple_probe(struct platform_device *pdev)
ret = reset_control_reset(simple->resets);
if (ret)
goto err_resetc_put;
- } else {
+ }
+
+ if (simple->need_reset) {
+ ret = reset_control_assert(simple->resets);
+ if (ret)
+ goto err_resetc_put;
+
+ usleep_range(1000, 2000);
+
ret = reset_control_deassert(simple->resets);
if (ret)
goto err_resetc_put;
@@ -121,9 +129,6 @@ static int dwc3_of_simple_remove(struct platform_device *pdev)
clk_bulk_put_all(simple->num_clocks, simple->clks);
simple->num_clocks = 0;
- if (!simple->pulse_resets)
- reset_control_assert(simple->resets);
-
reset_control_put(simple->resets);
pm_runtime_disable(dev);
Can you test?
--
balbi
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH] pinctrl: st: Add of_node_put() before return
From: Nishka Dasgupta @ 2019-08-15 6:06 UTC (permalink / raw)
To: patrice.chotard, linus.walleij, linux-arm-kernel, linux-gpio
Cc: Nishka Dasgupta
Each iteration of for_each_child_of_node puts the previous node, but in
the case of a return from the middle of the loop, there is no put, thus
causing a memory leak. Hence add an of_node_put before the return in
three places.
Issue found with Coccinelle.
Signed-off-by: Nishka Dasgupta <nishkadg.linux@gmail.com>
---
drivers/pinctrl/pinctrl-st.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/drivers/pinctrl/pinctrl-st.c b/drivers/pinctrl/pinctrl-st.c
index b9688ea548da..e788c7495ec7 100644
--- a/drivers/pinctrl/pinctrl-st.c
+++ b/drivers/pinctrl/pinctrl-st.c
@@ -1262,8 +1262,10 @@ static int st_pctl_parse_functions(struct device_node *np,
grp = &info->groups[*grp_index];
*grp_index += 1;
ret = st_pctl_dt_parse_groups(child, grp, info, i++);
- if (ret)
+ if (ret) {
+ of_node_put(child);
return ret;
+ }
}
dev_info(info->dev, "Function[%d\t name:%s,\tgroups:%d]\n",
index, func->name, func->ngroups);
@@ -1623,8 +1625,10 @@ static int st_pctl_probe_dt(struct platform_device *pdev,
if (of_property_read_bool(child, "gpio-controller")) {
const char *bank_name = NULL;
ret = st_gpiolib_register_bank(info, bank, child);
- if (ret)
+ if (ret) {
+ of_node_put(child);
return ret;
+ }
k = info->banks[bank].range.pin_base;
bank_name = info->banks[bank].range.name;
@@ -1641,6 +1645,7 @@ static int st_pctl_probe_dt(struct platform_device *pdev,
i++, &grp_index);
if (ret) {
dev_err(&pdev->dev, "No functions found.\n");
+ of_node_put(child);
return ret;
}
}
--
2.19.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH] pinctrl: meson: meson: Add of_node_put() before return
From: Nishka Dasgupta @ 2019-08-15 6:07 UTC (permalink / raw)
To: khilman, linus.walleij, linux-arm-kernel, linux-gpio,
linux-amlogic
Cc: Nishka Dasgupta
Each iteration of for_each_child_of_node puts the previous node, but in
the case of a return from the middle of the loop, there is no put, thus
causing a memory leak. Hence add an of_node_put before the return.
Issue found with Coccinelle.
Signed-off-by: Nishka Dasgupta <nishkadg.linux@gmail.com>
---
drivers/pinctrl/meson/pinctrl-meson.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/pinctrl/meson/pinctrl-meson.c b/drivers/pinctrl/meson/pinctrl-meson.c
index 596786926209..8bba9d053d9f 100644
--- a/drivers/pinctrl/meson/pinctrl-meson.c
+++ b/drivers/pinctrl/meson/pinctrl-meson.c
@@ -651,6 +651,7 @@ static int meson_pinctrl_parse_dt(struct meson_pinctrl *pc,
continue;
if (gpio_np) {
dev_err(pc->dev, "multiple gpio nodes\n");
+ of_node_put(np);
return -EINVAL;
}
gpio_np = np;
--
2.19.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH] pinctrl: samsung: samsung: Add of_node_put() before return
From: Nishka Dasgupta @ 2019-08-15 6:09 UTC (permalink / raw)
To: tomasz.figa, krzk, linus.walleij, linux-arm-kernel, linux-gpio,
linux-samsung-soc
Cc: Nishka Dasgupta
Each iteration of for_each_child_of_node puts the previous node, but in
the case of a return from the middle of the loop, there is no put, thus
causing a memory leak. Hence add an of_node_put before the return in
three places.
Issue found with Coccinelle.
Signed-off-by: Nishka Dasgupta <nishkadg.linux@gmail.com>
---
drivers/pinctrl/samsung/pinctrl-samsung.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/drivers/pinctrl/samsung/pinctrl-samsung.c b/drivers/pinctrl/samsung/pinctrl-samsung.c
index de0477bb469d..b24ac8f61ccd 100644
--- a/drivers/pinctrl/samsung/pinctrl-samsung.c
+++ b/drivers/pinctrl/samsung/pinctrl-samsung.c
@@ -272,6 +272,7 @@ static int samsung_dt_node_to_map(struct pinctrl_dev *pctldev,
&reserved_maps, num_maps);
if (ret < 0) {
samsung_dt_free_map(pctldev, *map, *num_maps);
+ of_node_put(np);
return ret;
}
}
@@ -785,8 +786,10 @@ static struct samsung_pmx_func *samsung_pinctrl_create_functions(
if (!of_get_child_count(cfg_np)) {
ret = samsung_pinctrl_create_function(dev, drvdata,
cfg_np, func);
- if (ret < 0)
+ if (ret < 0) {
+ of_node_put(cfg_np);
return ERR_PTR(ret);
+ }
if (ret > 0) {
++func;
++func_cnt;
@@ -797,8 +800,10 @@ static struct samsung_pmx_func *samsung_pinctrl_create_functions(
for_each_child_of_node(cfg_np, func_np) {
ret = samsung_pinctrl_create_function(dev, drvdata,
func_np, func);
- if (ret < 0)
+ if (ret < 0) {
+ of_node_put(func_np);
return ERR_PTR(ret);
+ }
if (ret > 0) {
++func;
++func_cnt;
--
2.19.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* Re: [PATCH] pinctrl: samsung: samsung: Add of_node_put() before return
From: Krzysztof Kozlowski @ 2019-08-15 6:15 UTC (permalink / raw)
To: Nishka Dasgupta
Cc: linux-gpio, linus.walleij, linux-samsung-soc@vger.kernel.org,
Tomasz Figa, linux-arm-kernel
In-Reply-To: <20190815060914.3572-1-nishkadg.linux@gmail.com>
On Thu, 15 Aug 2019 at 08:09, Nishka Dasgupta <nishkadg.linux@gmail.com> wrote:
>
> Each iteration of for_each_child_of_node puts the previous node, but in
> the case of a return from the middle of the loop, there is no put, thus
> causing a memory leak. Hence add an of_node_put before the return in
> three places.
> Issue found with Coccinelle.
>
> Signed-off-by: Nishka Dasgupta <nishkadg.linux@gmail.com>
> ---
> drivers/pinctrl/samsung/pinctrl-samsung.c | 9 +++++++--
> 1 file changed, 7 insertions(+), 2 deletions(-)
This is already fixed. You can base your patches on top of linux-next
to avoid duplicating effort.
Best regards,
Krzysztof
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH 0/6] drm+dma: cache support for arm, etc
From: Christoph Hellwig @ 2019-08-15 6:51 UTC (permalink / raw)
To: Rob Clark
Cc: Kate Stewart, Masayoshi Mizuma, Maciej W. Rozycki, Eric Biggers,
Catalin Marinas, Imre Deak, dri-devel, Chris Wilson,
Masahiro Yamada, Benjamin Gaignard, Mauro Carvalho Chehab,
Will Deacon, Christoph Hellwig, Emil Velikov, Rob Clark,
Michael Ellerman, Mike Rapoport, Geert Uytterhoeven,
moderated list:ARM64 PORT (AARCH64 ARCHITECTURE), Daniel Vetter,
open list:MIPS, Linus Walleij, Robin Murphy,
open list:DRM DRIVER FOR MSM ADRENO GPU, Deepak Sharma,
Joerg Roedel, Arnd Bergmann, Anshuman Khandual, Hauke Mehrtens,
Jesper Dangaard Brouer, Wolfram Sang (Renesas),
open list:LINUX FOR POWERPC (32-BIT AND 64-BIT), Alexios Zavras,
Russell King, Doug Anderson, Thomas Gleixner, Sean Paul,
Allison Randal, Christophe Leroy, Enrico Weigelt, Ard Biesheuvel,
Greg Kroah-Hartman, open list, Paul Burton, Souptick Joarder,
Andrew Morton, open list:DRM DRIVER FOR MSM ADRENO GPU
In-Reply-To: <20190814220011.26934-1-robdclark@gmail.com>
As said before I don't think these low-level helpers are the
right API to export, but even if they did you'd just cover a tiny
subset of the architectures.
Also to distil the previous thread - if you remap memory to uncached
the helper to use is arch_dma_prep_coherent, which does a writeback+
invalidate everywhere, and there is no need to clean up after a
long-term uncached mapping. We might still get speculations into
that area, if we don't remap the direct mapping, but it isn't like
invalidting that just before freeing the memory is going to help
anyone.
Also it seems like patches 5 and 6 are missing in my inbox.
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [GIT PULL] tee subsys for v5.4
From: Jens Wiklander @ 2019-08-15 6:56 UTC (permalink / raw)
To: arm-soc, soc; +Cc: Linux Kernel Mailing List, Linux ARM
Hello arm-soc maintainers,
Please pull this OP-TEE driver patch. It adds a call to might_sleep()
during RPC in the OP-TEE driver in order to be more friendly with
CONFIG_PREEMPT_VOLUNTARY.
Thanks,
Jens
The following changes since commit 0ecfebd2b52404ae0c54a878c872bb93363ada36:
Linux 5.2 (2019-07-07 15:41:56 -0700)
are available in the Git repository at:
git://git.linaro.org/people/jens.wiklander/linux-tee.git tags/tee-optee-for-5.4
for you to fetch changes up to 9f02b8f61f29f4518581770d57bfffe99b1ea599:
tee: optee: add might_sleep for RPC requests (2019-07-08 22:38:56 +0200)
----------------------------------------------------------------
Add might_sleep() in OP-TEE RPC requests
----------------------------------------------------------------
Rouven Czerwinski (1):
tee: optee: add might_sleep for RPC requests
drivers/tee/optee/call.c | 1 +
1 file changed, 1 insertion(+)
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH 21/22] ARM: omap1: use common clk framework
From: Arnd Bergmann @ 2019-08-15 7:10 UTC (permalink / raw)
To: Aaro Koskinen
Cc: Paul Walmsley, Bartlomiej Zolnierkiewicz, Tony Lindgren,
Greg Kroah-Hartman, Linus Walleij, Linux Kernel Mailing List,
Tomi Valkeinen, linux-omap, Linux ARM
In-Reply-To: <20190814211002.GA1952@darkstar.musicnaut.iki.fi>
On Wed, Aug 14, 2019 at 11:10 PM Aaro Koskinen <aaro.koskinen@iki.fi> wrote:
>
> Hi,
>
> On Thu, Aug 08, 2019 at 11:43:39PM +0200, Arnd Bergmann wrote:
> > The omap1 clock driver now uses types and calling conventions
> > that are compatible with the common clk core.
> >
> > Turn on CONFIG_COMMON_CLK and remove all the code that is
> > now duplicated.
> >
> > Note: if this previous steps didn't already break it, this one
> > most likely will, because the interfaces are very likely to
> > have different semantics.
>
> QEMU SX1 board works up to this patch (the I/O virtual address change
> included). With this patch, it seems to fail to allocate memory during
> omap1_init_early() (the log is a bit messy as I extracted it using QEMU
> memory dumping):
That sounds pretty good, I definitely did not expect this patch
to work without first dealing with a few bugs, and it it did not break
earlier, I'm willing to call that success ;-)
Unfortunately, doing it in qemu does not guarantee that the clocks
are set up right at this point: if any of the clocks are disabled when
they should not be, qemu won't care as much as real hardware would.
> swapper: page allocation failure: order:0, mode:0x0(), nodemask=(null)
> CPU: 0 PID: 0 Comm: swapper Not tainted 5.3.0-rc4-sx1-los_80efa++ #1
> Hardware name: OMAP310 based Siemens SX1
> [<c000dc44>] (unwind_backtrace) from [<c000cb00>] (show_stack+0x10/0x18)
> [<c000cb00>] (show_stack) from [<c0172ba8>] (dump_stack+0x18/0x24)
> [<c0172ba8>] (dump_stack) from [<c00844e8>] (warn_alloc+0x90/0x140)
> [<c00844e8>] (warn_alloc) from [<c0084dcc>] (__alloc_pages_nodemask+0x7a4/0x9cc)
> [<c0084dcc>] (__alloc_pages_nodemask) from [<c008df24>] (slob_new_pages.constpro
> p.2+0x10/0x3c)
> [<c008df24>] (slob_new_pages.constprop.2) from [<c008e208>] (slob_alloc.constprop.1+0xe4/0x1e8)
> [<c008e208>] (slob_alloc.constprop.1) from [<c008e344>] (__kmalloc+0x38/0xb0)
> [<c008e344>] (__kmalloc) from [<c0126514>] (__clk_register+0x20/0x62c)
> [<c0126514>] (__clk_register) from [<c01f6614>] (omap1_clk_init+0x88/0x220)
> [<c01f6614>] (omap1_clk_init) from [<c01f5820>] (omap1_init_early+0x20/0x30)
> [<c01f5820>] (omap1_init_early) from [<c01f09e8>] (start_kernel+0x48/0x408)
> [<c01f09e8>] (start_kernel) from [<00000000>] (0x0)
> Clocks: ARM_SYSST: 0x003a DPLL_CTL: 0x2002 ARM_CKCTL: 0x3000
> Clocking rate (xtal/DPLL1/MPU): 12.0/12.0/0.0 MHz
Ok, so here the problem is that we call the omap1_clk_init() function from
setup_arch(), which is before we can even allocate memory with kmalloc.
Most other machines do it from init_time(), which comes after the initialization
of the memory allocator.
Something like this would be needed:
diff --git a/arch/arm/mach-omap1/io.c b/arch/arm/mach-omap1/io.c
index b0465a956ea8..17ba8dfd8e19 100644
--- a/arch/arm/mach-omap1/io.c
+++ b/arch/arm/mach-omap1/io.c
@@ -125,9 +125,6 @@ void __init omap1_init_early(void)
omap_writew(0x0, MPU_PUBLIC_TIPB_CNTL);
omap_writew(0x0, MPU_PRIVATE_TIPB_CNTL);
- /* Must init clocks early to assure that timer interrupt works
- */
- omap1_clk_init();
omap1_mux_init();
}
diff --git a/arch/arm/mach-omap1/time.c b/arch/arm/mach-omap1/time.c
index 7cc1a968230e..4e5ddd1db429 100644
--- a/arch/arm/mach-omap1/time.c
+++ b/arch/arm/mach-omap1/time.c
@@ -228,6 +228,8 @@ static inline void omap_mpu_timer_init(void)
*/
void __init omap1_timer_init(void)
{
+ omap1_clk_init();
+
if (omap_32k_timer_init() != 0)
omap_mpu_timer_init();
}
but the removed comment up there makes me suspect that it introduces
a different issue.
> "8<--- cut here ---
> "Unable to handle kernel NULL pointer dereference at virtual address 00000018
> "pgd = (ptrval)
> "[00000018] *pgd=00000000
> Internal error: Oops: 5 [#1] ARM
> CPU: 0 PID: 0 Comm: swapper Not tainted 5.3.0-rc4-sx1-los_80efa++ #1
> Hardware name: OMAP310 based Siemens SX1
> PC is at clk_hw_get_parent+0x4/0x14
> LR is at omap1_clk_enable+0xc/0xcc
> OMAP310 based Siemens SX1
> [ 0.000000] free:0 free_pcp:0 free_cma:0
> pc : [<c0126cd0>] lr : [<c00128d4>] psr: 600001d3
> sp : c03aff88 ip : 00000000 fp : 00000000
> r10: 00000001 r9 : 54029252 r8 : 10000100
> r7 : c03b1000 r6 : 00002002 r5 : 0000003a r4 : c03b5444
> r3 : 00000000 r2 : c03b9818 r1 : ff03ce08 r0 : c03b5444
> Flags: nZCv IRQs off FIQs off Mode SVC_32 ISA ARM Segment user
> Control: 0000317f Table: 10004000 DAC: 00000055
> Process swapper (pid: 0, stack limit = 0x(ptrval))
> Stack: (0xc03aff88 to 0xc03b0000)
> ff80: c03b5438 0000003a 00002002 c01f6734 00000000 00000057
> ffa0: 0000313d c01f5820 00000000 c01f09e8 00000000 00000000 00000000 00000000
> ffc0: 00000000 00000000 00000000 c0201a38 00000000 c01f0330 00000057 0000313d
> ffe0: 00000265 10000100 54029252 0000317f 00000000 00000000 00000000 00000000
> [<c0126cd0>] (clk_hw_get_parent) from [<c00128d4>] (omap1_clk_enable+0xc/0xcc)
> [<c00128d4>] (omap1_clk_enable) from [<c01f6734>] (omap1_clk_init+0x1a8/0x220)
> [<c01f6734>] (omap1_clk_init) from [<c01f5820>] (omap1_init_early+0x20/0x30)
> [<c01f5820>] (omap1_init_early) from [<c01f09e8>] (start_kernel+0x48/0x408)
> [<c01f09e8>] (start_kernel) from [<00000000>] (0x0)
clk_hw->core is NULL here, presumably as a result of the first issue.
Arnd
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v16 0/5] Add support for mt8183 SCP.
From: Pi-Hsun Shih @ 2019-08-15 7:16 UTC (permalink / raw)
Cc: open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
open list:REMOTE PROCESSOR REMOTEPROC SUBSYSTEM, open list,
moderated list:ARM/Mediatek SoC support, Pi-Hsun Shih,
moderated list:ARM/Mediatek SoC support
Add support for controlling and communicating with mt8183's system
control processor (SCP), using the remoteproc & rpmsg framework.
And also add a cros_ec driver for CrOS EC host command over rpmsg.
The overall structure of the series is:
* remoteproc/mtk_scp.c: Control the start / stop of SCP (Patch 2, 3).
* remoteproc/mtk_scp_ipi.c: Communicates to SCP using inter-processor
interrupt (IPI) and shared memory (Patch 2, 3).
* rpmsg/mtk_rpmsg.c: Wrapper to wrap the IPI communication into a rpmsg
device. Supports name service for SCP firmware to
announce channels (Patch 4).
* add scp dts node to mt8183 platform (Patch 5).
Changes from v15:
- Fix a bug on incorrect usage of wait_event_timeout return value.
Changes from v14:
- Fix a typo on variable in DEBUG section.
Changes from v13:
- Move include/linux/platform_data/mtk_scp.h to
include/linux/remoteproc/mtk_scp.h.
- Rename scp_get_reserve_* to scp_get_reserved_*.
- Add lock for access of scp->ipi_desc.
- Lock the whole ipi_send function.
- Move more setting of cache size from SCP firmware to kernel driver,
to prevent problem while loading firmware onto DRAM.
- Minor fixes addressing comment.
Changes from v12:
- Initialize cache before firmware load, to avoid problem while loading
large firmware.
- Disable watchdog before stopping SCP, to avoid extra warning message.
- Fix new warnings by checkpatch.
Changes from v11:
- Fixed a bug that mtk_rpmsg_endpoint is not properly cleaned up if
rproc_boot fails.
- Add missing documentation in comment.
Changes from v10:
- Drop applied cros_ec_rpmsg patches.
- Add clock reset before loading SCP firmware.
- Fix some type mismatch warnings when printing debug messages.
Changes from v9:
- Remove reserve-memory-vpu_share node.
- Remove change to cros_ec_commands.h (That is already in
https://lore.kernel.org/lkml/20190518063949.GY4319@dell/T/)
Changes from v8:
- Rebased onto https://patchwork.kernel.org/cover/10962385/.
- Drop merged cros_ec_rpmsg patch, and add scp dts node patch.
- Add more reserved memory region.
Changes from v7:
- Rebase onto https://lore.kernel.org/patchwork/patch/1059196/.
- Fix clock enable/disable timing for SCP driver.
- Add more SCP IPI ID.
Changes from v6:
- Decouple mtk_rpmsg from mtk_scp.
- Change data of EC response to be aligned to 4 bytes.
Changes from v5:
- Add device tree binding document for cros_ec_rpmsg.
- Better document in comments for cros_ec_rpmsg.
- Remove dependency on CONFIG_ in binding tree document.
Changes from v4:
- Merge patch 6 (Load ELF firmware) into patch 2, so the driver loads
ELF firmware by default, and no longer accept plain binary.
- rpmsg_device listed in device tree (as a child of the SCP node) would
have it's device tree node mapped to the rpmsg_device, so the rpmsg
driver can use the properties on device tree.
Changes from v3:
- Make writing to SCP SRAM aligned.
- Add a new patch (Patch 6) to load ELF instead of bin firmware.
- Add host event support for EC driver.
- Fix some bugs found in testing (missing spin_lock_init,
rproc_subdev_unprepare to rproc_subdev_stop).
- Fix some coding style issue found by checkpatch.pl.
Changes from v2:
- Fold patch 3 into patch 2 in v2.
- Move IPI id around to support cross-testing for old and new firmware.
- Finish more TODO items.
Changes from v1:
- Extract functions and rename variables in mtk_scp.c.
- Do cleanup properly in mtk_rpmsg.c, which also removes the problem of
short-lived work items.
- Code format fix based on feedback for cros_ec_rpmsg.c.
- Extract feature detection for SCP into separate patch (Patch 6).
Eddie Huang (1):
arm64: dts: mt8183: add scp node
Erin Lo (3):
dt-bindings: Add a binding for Mediatek SCP
remoteproc/mediatek: add SCP support for mt8183
remoteproc: mt8183: add reserved memory manager API
Pi-Hsun Shih (1):
rpmsg: add rpmsg support for mt8183 SCP.
.../bindings/remoteproc/mtk,scp.txt | 36 +
arch/arm64/boot/dts/mediatek/mt8183-evb.dts | 11 +
arch/arm64/boot/dts/mediatek/mt8183.dtsi | 12 +
drivers/remoteproc/Kconfig | 10 +
drivers/remoteproc/Makefile | 1 +
drivers/remoteproc/mtk_common.h | 92 +++
drivers/remoteproc/mtk_scp.c | 710 ++++++++++++++++++
drivers/remoteproc/mtk_scp_ipi.c | 161 ++++
drivers/rpmsg/Kconfig | 9 +
drivers/rpmsg/Makefile | 1 +
drivers/rpmsg/mtk_rpmsg.c | 414 ++++++++++
include/linux/remoteproc/mtk_scp.h | 168 +++++
include/linux/rpmsg/mtk_rpmsg.h | 38 +
13 files changed, 1663 insertions(+)
create mode 100644 Documentation/devicetree/bindings/remoteproc/mtk,scp.txt
create mode 100644 drivers/remoteproc/mtk_common.h
create mode 100644 drivers/remoteproc/mtk_scp.c
create mode 100644 drivers/remoteproc/mtk_scp_ipi.c
create mode 100644 drivers/rpmsg/mtk_rpmsg.c
create mode 100644 include/linux/remoteproc/mtk_scp.h
create mode 100644 include/linux/rpmsg/mtk_rpmsg.h
--
2.23.0.rc1.153.gdeed80330f-goog
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [PATCH v16 1/5] dt-bindings: Add a binding for Mediatek SCP
From: Pi-Hsun Shih @ 2019-08-15 7:16 UTC (permalink / raw)
Cc: Ohad Ben-Cohen, Rob Herring,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
Erin Lo, open list:REMOTE PROCESSOR REMOTEPROC SUBSYSTEM,
open list, Bjorn Andersson, Rob Herring,
moderated list:ARM/Mediatek SoC support, Pi-Hsun Shih,
Matthias Brugger, Mark Rutland,
moderated list:ARM/Mediatek SoC support
In-Reply-To: <20190815071635.168671-1-pihsun@chromium.org>
From: Erin Lo <erin.lo@mediatek.com>
Add a DT binding documentation of SCP for the
MT8183 SoC from Mediatek.
Signed-off-by: Erin Lo <erin.lo@mediatek.com>
Signed-off-by: Pi-Hsun Shih <pihsun@chromium.org>
Reviewed-by: Rob Herring <robh@kernel.org>
---
Changes from v15, v14, v13, v12, v11, v10, v9, v8, v7, v6:
- No change.
Changes from v5:
- Remove dependency on CONFIG_RPMSG_MTK_SCP.
Changes from v4:
- Add detail of more properties.
- Document the usage of mtk,rpmsg-name in subnode from the new design.
Changes from v3:
- No change.
Changes from v2:
- No change. I realized that for this patch series, there's no need to
add anything under the mt8183-scp node (neither the mt8183-rpmsg or
the cros-ec-rpmsg) for them to work, since mt8183-rpmsg is added
directly as a rproc_subdev by code, and cros-ec-rpmsg is dynamically
created by SCP name service.
Changes from v1:
- No change.
---
.../bindings/remoteproc/mtk,scp.txt | 36 +++++++++++++++++++
1 file changed, 36 insertions(+)
create mode 100644 Documentation/devicetree/bindings/remoteproc/mtk,scp.txt
diff --git a/Documentation/devicetree/bindings/remoteproc/mtk,scp.txt b/Documentation/devicetree/bindings/remoteproc/mtk,scp.txt
new file mode 100644
index 000000000000..3ba668bab14b
--- /dev/null
+++ b/Documentation/devicetree/bindings/remoteproc/mtk,scp.txt
@@ -0,0 +1,36 @@
+Mediatek SCP Bindings
+----------------------------------------
+
+This binding provides support for ARM Cortex M4 Co-processor found on some
+Mediatek SoCs.
+
+Required properties:
+- compatible Should be "mediatek,mt8183-scp"
+- reg Should contain the address ranges for the two memory
+ regions, SRAM and CFG.
+- reg-names Contains the corresponding names for the two memory
+ regions. These should be named "sram" & "cfg".
+- clocks Clock for co-processor (See: ../clock/clock-bindings.txt)
+- clock-names Contains the corresponding name for the clock. This
+ should be named "main".
+
+Subnodes
+--------
+
+Subnodes of the SCP represent rpmsg devices. The names of the devices are not
+important. The properties of these nodes are defined by the individual bindings
+for the rpmsg devices - but must contain the following property:
+
+- mtk,rpmsg-name Contains the name for the rpmsg device. Used to match
+ the subnode to rpmsg device announced by SCP.
+
+Example:
+
+ scp: scp@10500000 {
+ compatible = "mediatek,mt8183-scp";
+ reg = <0 0x10500000 0 0x80000>,
+ <0 0x105c0000 0 0x5000>;
+ reg-names = "sram", "cfg";
+ clocks = <&infracfg CLK_INFRA_SCPSYS>;
+ clock-names = "main";
+ };
--
2.23.0.rc1.153.gdeed80330f-goog
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v16 2/5] remoteproc/mediatek: add SCP support for mt8183
From: Pi-Hsun Shih @ 2019-08-15 7:16 UTC (permalink / raw)
Cc: Ohad Ben-Cohen, Nicolas Boichat, Erin Lo,
open list:REMOTE PROCESSOR REMOTEPROC SUBSYSTEM, open list,
Bjorn Andersson, moderated list:ARM/Mediatek SoC support,
Pi-Hsun Shih, Matthias Brugger,
moderated list:ARM/Mediatek SoC support
In-Reply-To: <20190815071635.168671-1-pihsun@chromium.org>
From: Erin Lo <erin.lo@mediatek.com>
Provide a basic driver to control Cortex M4 co-processor
Signed-off-by: Erin Lo <erin.lo@mediatek.com>
Signed-off-by: Nicolas Boichat <drinkcat@chromium.org>
Signed-off-by: Pi-Hsun Shih <pihsun@chromium.org>
---
Changes from v15:
- Fix a bug on incorrect usage of wait_event_timeout return value.
Changes from v14:
- No change.
Changes from v13:
- Move include/linux/platform_data/mtk_scp.h to
include/linux/remoteproc/mtk_scp.h.
- Add lock for access of scp->ipi_desc.
- Lock the whole ipi_send function.
- Move more setting of cache size from SCP firmware to kernel driver,
to prevent problem while loading firmware onto DRAM.
- Cleanup and remove unused branch in scp_da_to_va.
- Minor fixes addressing comment.
Changes from v12:
- Initialize cache before firmware load, to avoid problem while loading
large firmware.
- Disable watchdog before stopping SCP, to avoid extra warning message.
- Use strscpy instead of strncpy.
Changes from v11:
- No change.
Changes from v10:
- Add a clock reset before loading firmware.
Changes from v9:
- No change.
Changes from v8:
- Add a missing space.
Changes from v7:
- Moved the location of shared SCP buffer.
- Fix clock enable/disable sequence.
- Add more IPI ID that would be used.
Changes from v6:
- No change.
Changes from v5:
- Changed some space to tab.
Changes from v4:
- Rename most function from mtk_scp_* to scp_*.
- Change the irq to threaded handler.
- Load ELF file instead of plain binary file as firmware by default
(Squashed patch 6 in v4 into this patch).
Changes from v3:
- Fix some issue found by checkpatch.
- Make writes aligned in scp_ipi_send.
Changes from v2:
- Squash patch 3 from v2 (separate the ipi interface) into this patch.
- Remove unused name argument from scp_ipi_register.
- Add scp_ipi_unregister for proper cleanup.
- Move IPI ids in sync with firmware.
- Add mb() in proper place, and correctly clear the run->signaled.
Changes from v1:
- Extract functions and rename variables in mtk_scp.c.
---
drivers/remoteproc/Kconfig | 9 +
drivers/remoteproc/Makefile | 1 +
drivers/remoteproc/mtk_common.h | 90 +++++
drivers/remoteproc/mtk_scp.c | 535 +++++++++++++++++++++++++++++
drivers/remoteproc/mtk_scp_ipi.c | 160 +++++++++
include/linux/remoteproc/mtk_scp.h | 141 ++++++++
6 files changed, 936 insertions(+)
create mode 100644 drivers/remoteproc/mtk_common.h
create mode 100644 drivers/remoteproc/mtk_scp.c
create mode 100644 drivers/remoteproc/mtk_scp_ipi.c
create mode 100644 include/linux/remoteproc/mtk_scp.h
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index 28ed306982f7..ea71cad399f7 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -23,6 +23,15 @@ config IMX_REMOTEPROC
It's safe to say N here.
+config MTK_SCP
+ tristate "Mediatek SCP support"
+ depends on ARCH_MEDIATEK
+ help
+ Say y here to support Mediatek's System Companion Processor (SCP) via
+ the remote processor framework.
+
+ It's safe to say N here.
+
config OMAP_REMOTEPROC
tristate "OMAP remoteproc support"
depends on ARCH_OMAP4 || SOC_OMAP5
diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
index 00f09e658cb3..e30a1b15fbac 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_IMX_REMOTEPROC) += imx_rproc.o
+obj-$(CONFIG_MTK_SCP) += mtk_scp.o mtk_scp_ipi.o
obj-$(CONFIG_OMAP_REMOTEPROC) += omap_remoteproc.o
obj-$(CONFIG_WKUP_M3_RPROC) += wkup_m3_rproc.o
obj-$(CONFIG_DA8XX_REMOTEPROC) += da8xx_remoteproc.o
diff --git a/drivers/remoteproc/mtk_common.h b/drivers/remoteproc/mtk_common.h
new file mode 100644
index 000000000000..e213039543ec
--- /dev/null
+++ b/drivers/remoteproc/mtk_common.h
@@ -0,0 +1,90 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 MediaTek Inc.
+ */
+
+#ifndef __RPROC_MTK_COMMON_H
+#define __RPROC_MTK_COMMON_H
+
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/remoteproc.h>
+
+#define MT8183_SW_RSTN 0x0
+#define MT8183_SW_RSTN_BIT BIT(0)
+#define MT8183_SCP_TO_HOST 0x1C
+#define MT8183_SCP_IPC_INT_BIT BIT(0)
+#define MT8183_SCP_WDT_INT_BIT BIT(8)
+#define MT8183_HOST_TO_SCP 0x28
+#define MT8183_HOST_IPC_INT_BIT BIT(0)
+#define MT8183_WDT_CFG 0x84
+#define MT8183_SCP_CLK_SW_SEL 0x4000
+#define MT8183_SCP_CLK_DIV_SEL 0x4024
+#define MT8183_SCP_SRAM_PDN 0x402C
+#define MT8183_SCP_L1_SRAM_PD 0x4080
+#define MT8183_SCP_TCM_TAIL_SRAM_PD 0x4094
+
+#define MT8183_SCP_CACHE_SEL(x) (0x14000 + (x) * 0x3000)
+#define MT8183_SCP_CACHE_CON MT8183_SCP_CACHE_SEL(0)
+#define MT8183_SCP_DCACHE_CON MT8183_SCP_CACHE_SEL(1)
+#define MT8183_SCP_CACHESIZE_8KB BIT(8)
+#define MT8183_SCP_CACHE_CON_WAYEN BIT(10)
+
+#define SCP_FW_VER_LEN 32
+#define SCP_SHARE_BUFFER_SIZE 288
+
+struct scp_run {
+ u32 signaled;
+ s8 fw_ver[SCP_FW_VER_LEN];
+ u32 dec_capability;
+ u32 enc_capability;
+ wait_queue_head_t wq;
+};
+
+struct scp_ipi_desc {
+ scp_ipi_handler_t handler;
+ void *priv;
+};
+
+struct mtk_scp {
+ struct device *dev;
+ struct rproc *rproc;
+ struct clk *clk;
+ void __iomem *reg_base;
+ void __iomem *sram_base;
+ size_t sram_size;
+
+ struct share_obj *recv_buf;
+ struct share_obj *send_buf;
+ struct scp_run run;
+ /* To prevent multiple ipi_send run concurrently. */
+ struct mutex send_lock;
+ /* For protecting ipi_desc field. */
+ struct mutex desc_lock;
+ struct scp_ipi_desc ipi_desc[SCP_IPI_MAX];
+ bool ipi_id_ack[SCP_IPI_MAX];
+ wait_queue_head_t ack_wq;
+
+ void __iomem *cpu_addr;
+ phys_addr_t phys_addr;
+ size_t dram_size;
+};
+
+/**
+ * struct share_obj - SRAM buffer shared with
+ * AP and SCP
+ *
+ * @id: IPI id
+ * @len: share buffer length
+ * @share_buf: share buffer data
+ */
+struct share_obj {
+ u32 id;
+ u32 len;
+ u8 share_buf[SCP_SHARE_BUFFER_SIZE];
+};
+
+void scp_memcpy_aligned(void *dst, const void *src, unsigned int len);
+
+#endif
diff --git a/drivers/remoteproc/mtk_scp.c b/drivers/remoteproc/mtk_scp.c
new file mode 100644
index 000000000000..2e4fa3e41f84
--- /dev/null
+++ b/drivers/remoteproc/mtk_scp.c
@@ -0,0 +1,535 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (c) 2019 MediaTek Inc.
+
+#include <asm/barrier.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/remoteproc.h>
+#include <linux/remoteproc/mtk_scp.h>
+
+#include "mtk_common.h"
+#include "remoteproc_internal.h"
+
+#define MAX_CODE_SIZE 0x500000
+#define SCP_FW_END 0x7C000
+
+struct platform_device *scp_get_pdev(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *scp_node;
+ struct platform_device *scp_pdev;
+
+ scp_node = of_parse_phandle(dev->of_node, "mediatek,scp", 0);
+ if (!scp_node) {
+ dev_err(dev, "can't get SCP node\n");
+ return NULL;
+ }
+
+ scp_pdev = of_find_device_by_node(scp_node);
+ if (WARN_ON(!scp_pdev)) {
+ dev_err(dev, "SCP pdev failed\n");
+ of_node_put(scp_node);
+ return NULL;
+ }
+
+ return scp_pdev;
+}
+EXPORT_SYMBOL_GPL(scp_get_pdev);
+
+static void scp_wdt_handler(struct mtk_scp *scp, u32 scp_to_host)
+{
+ dev_err(scp->dev, "SCP watchdog timeout! 0x%x", scp_to_host);
+ rproc_report_crash(scp->rproc, RPROC_WATCHDOG);
+}
+
+static void scp_init_ipi_handler(void *data, unsigned int len, void *priv)
+{
+ struct mtk_scp *scp = (struct mtk_scp *)priv;
+ struct scp_run *run = (struct scp_run *)data;
+
+ scp->run.signaled = run->signaled;
+ strscpy(scp->run.fw_ver, run->fw_ver, SCP_FW_VER_LEN);
+ scp->run.dec_capability = run->dec_capability;
+ scp->run.enc_capability = run->enc_capability;
+ wake_up_interruptible(&scp->run.wq);
+}
+
+static void scp_ipi_handler(struct mtk_scp *scp)
+{
+ struct share_obj *rcv_obj = scp->recv_buf;
+ struct scp_ipi_desc *ipi_desc = scp->ipi_desc;
+ u8 tmp_data[SCP_SHARE_BUFFER_SIZE];
+ scp_ipi_handler_t handler;
+
+ if (rcv_obj->len > SCP_SHARE_BUFFER_SIZE) {
+ dev_err(scp->dev, "ipi message too long (len %d, max %d)",
+ rcv_obj->len, SCP_SHARE_BUFFER_SIZE);
+ return;
+ }
+ mutex_lock(&scp->desc_lock);
+ handler = ipi_desc[rcv_obj->id].handler;
+ mutex_unlock(&scp->desc_lock);
+ if (rcv_obj->id >= SCP_IPI_MAX || !handler) {
+ dev_err(scp->dev, "No such ipi id = %d\n", rcv_obj->id);
+ return;
+ }
+
+ memcpy_fromio(tmp_data, &rcv_obj->share_buf, rcv_obj->len);
+ handler(tmp_data, rcv_obj->len, ipi_desc[rcv_obj->id].priv);
+ scp->ipi_id_ack[rcv_obj->id] = true;
+ wake_up(&scp->ack_wq);
+}
+
+static int scp_ipi_init(struct mtk_scp *scp)
+{
+ size_t send_offset = SCP_FW_END - sizeof(struct share_obj);
+ size_t recv_offset = send_offset - sizeof(struct share_obj);
+
+ /* Disable SCP to host interrupt */
+ writel(MT8183_SCP_IPC_INT_BIT, scp->reg_base + MT8183_SCP_TO_HOST);
+
+ /* shared buffer initialization */
+ scp->recv_buf = (__force struct share_obj *)(scp->sram_base +
+ recv_offset);
+ scp->send_buf = (__force struct share_obj *)(scp->sram_base +
+ send_offset);
+ memset_io(scp->recv_buf, 0, sizeof(scp->recv_buf));
+ memset_io(scp->send_buf, 0, sizeof(scp->send_buf));
+
+ return 0;
+}
+
+static void scp_reset_assert(const struct mtk_scp *scp)
+{
+ u32 val;
+
+ val = readl(scp->reg_base + MT8183_SW_RSTN);
+ val &= ~MT8183_SW_RSTN_BIT;
+ writel(val, scp->reg_base + MT8183_SW_RSTN);
+}
+
+static void scp_reset_deassert(const struct mtk_scp *scp)
+{
+ u32 val;
+
+ val = readl(scp->reg_base + MT8183_SW_RSTN);
+ val |= MT8183_SW_RSTN_BIT;
+ writel(val, scp->reg_base + MT8183_SW_RSTN);
+}
+
+static irqreturn_t scp_irq_handler(int irq, void *priv)
+{
+ struct mtk_scp *scp = priv;
+ u32 scp_to_host;
+ int ret;
+
+ ret = clk_prepare_enable(scp->clk);
+ if (ret) {
+ dev_err(scp->dev, "failed to enable clocks\n");
+ return IRQ_NONE;
+ }
+
+ scp_to_host = readl(scp->reg_base + MT8183_SCP_TO_HOST);
+ if (scp_to_host & MT8183_SCP_IPC_INT_BIT)
+ scp_ipi_handler(scp);
+ else
+ scp_wdt_handler(scp, scp_to_host);
+
+ /*
+ * Ensure that all writes to SRAM are committed before another
+ * interrupt.
+ */
+ mb();
+ /* SCP won't send another interrupt until we set SCP_TO_HOST to 0. */
+ writel(MT8183_SCP_IPC_INT_BIT | MT8183_SCP_WDT_INT_BIT,
+ scp->reg_base + MT8183_SCP_TO_HOST);
+ clk_disable_unprepare(scp->clk);
+
+ return IRQ_HANDLED;
+}
+
+static int scp_elf_load_segments(struct rproc *rproc, const struct firmware *fw)
+{
+ struct device *dev = &rproc->dev;
+ struct elf32_hdr *ehdr;
+ struct elf32_phdr *phdr;
+ int i, ret = 0;
+ const u8 *elf_data = fw->data;
+
+ ehdr = (struct elf32_hdr *)elf_data;
+ phdr = (struct elf32_phdr *)(elf_data + ehdr->e_phoff);
+
+ /* go through the available ELF segments */
+ for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
+ u32 da = phdr->p_paddr;
+ u32 memsz = phdr->p_memsz;
+ u32 filesz = phdr->p_filesz;
+ u32 offset = phdr->p_offset;
+ void __iomem *ptr;
+
+ if (phdr->p_type != PT_LOAD)
+ continue;
+
+ dev_dbg(dev, "phdr: type %d da 0x%x memsz 0x%x filesz 0x%x\n",
+ phdr->p_type, da, memsz, filesz);
+
+ if (filesz > memsz) {
+ dev_err(dev, "bad phdr filesz 0x%x memsz 0x%x\n",
+ filesz, memsz);
+ ret = -EINVAL;
+ break;
+ }
+
+ if (offset + filesz > fw->size) {
+ dev_err(dev, "truncated fw: need 0x%x avail 0x%zx\n",
+ offset + filesz, fw->size);
+ ret = -EINVAL;
+ break;
+ }
+
+ /* grab the kernel address for this device address */
+ ptr = rproc_da_to_va(rproc, da, memsz);
+ if (!ptr) {
+ dev_err(dev, "bad phdr da 0x%x mem 0x%x\n", da, memsz);
+ ret = -EINVAL;
+ break;
+ }
+
+ /* put the segment where the remote processor expects it */
+ if (phdr->p_filesz)
+ scp_memcpy_aligned(ptr, elf_data + phdr->p_offset,
+ filesz);
+ }
+
+ return ret;
+}
+
+static int scp_load(struct rproc *rproc, const struct firmware *fw)
+{
+ const struct mtk_scp *scp = rproc->priv;
+ struct device *dev = scp->dev;
+ int ret;
+
+ ret = clk_prepare_enable(scp->clk);
+ if (ret) {
+ dev_err(dev, "failed to enable clocks\n");
+ return ret;
+ }
+
+ /* Hold SCP in reset while loading FW. */
+ scp_reset_assert(scp);
+
+ /* Reset clocks before loading FW */
+ writel(0x0, scp->reg_base + MT8183_SCP_CLK_SW_SEL);
+ writel(0x0, scp->reg_base + MT8183_SCP_CLK_DIV_SEL);
+
+ /* Initialize TCM before loading FW. */
+ writel(0x0, scp->reg_base + MT8183_SCP_L1_SRAM_PD);
+ writel(0x0, scp->reg_base + MT8183_SCP_TCM_TAIL_SRAM_PD);
+
+ /* Turn on the power of SCP's SRAM before using it. */
+ writel(0x0, scp->reg_base + MT8183_SCP_SRAM_PDN);
+
+ /*
+ * Set I-cache and D-cache size before loading SCP FW.
+ * SCP SRAM logical address may change when cache size setting differs.
+ */
+ writel(MT8183_SCP_CACHE_CON_WAYEN | MT8183_SCP_CACHESIZE_8KB,
+ scp->reg_base + MT8183_SCP_CACHE_CON);
+ writel(MT8183_SCP_CACHESIZE_8KB, scp->reg_base + MT8183_SCP_DCACHE_CON);
+
+ ret = scp_elf_load_segments(rproc, fw);
+ clk_disable_unprepare(scp->clk);
+
+ return ret;
+}
+
+static int scp_start(struct rproc *rproc)
+{
+ struct mtk_scp *scp = (struct mtk_scp *)rproc->priv;
+ struct device *dev = scp->dev;
+ struct scp_run *run = &scp->run;
+ int ret;
+
+ ret = clk_prepare_enable(scp->clk);
+ if (ret) {
+ dev_err(dev, "failed to enable clocks\n");
+ return ret;
+ }
+
+ run->signaled = false;
+
+ scp_reset_deassert(scp);
+
+ ret = wait_event_interruptible_timeout(
+ run->wq,
+ run->signaled,
+ msecs_to_jiffies(2000));
+
+ if (ret == 0) {
+ dev_err(dev, "wait SCP initialization timeout!\n");
+ ret = -ETIME;
+ goto stop;
+ }
+ if (ret == -ERESTARTSYS) {
+ dev_err(dev, "wait SCP interrupted by a signal!\n");
+ goto stop;
+ }
+ clk_disable_unprepare(scp->clk);
+ dev_info(dev, "SCP is ready. FW version %s\n", run->fw_ver);
+
+ return 0;
+
+stop:
+ scp_reset_assert(scp);
+ clk_disable_unprepare(scp->clk);
+ return ret;
+}
+
+static void *scp_da_to_va(struct rproc *rproc, u64 da, int len)
+{
+ struct mtk_scp *scp = (struct mtk_scp *)rproc->priv;
+ int offset;
+
+ if (da < scp->sram_size) {
+ offset = da;
+ if (offset >= 0 && (offset + len) < scp->sram_size)
+ return scp->sram_base + offset;
+ } else {
+ offset = da - scp->phys_addr;
+ if (offset >= 0 && (offset + len) < scp->dram_size)
+ return scp->cpu_addr + offset;
+ }
+
+ return NULL;
+}
+
+static int scp_stop(struct rproc *rproc)
+{
+ struct mtk_scp *scp = (struct mtk_scp *)rproc->priv;
+ int ret;
+
+ ret = clk_prepare_enable(scp->clk);
+ if (ret) {
+ dev_err(scp->dev, "failed to enable clocks\n");
+ return ret;
+ }
+
+ scp_reset_assert(scp);
+ /* Disable SCP watchdog */
+ writel(0, scp->reg_base + MT8183_WDT_CFG);
+ clk_disable_unprepare(scp->clk);
+
+ return 0;
+}
+
+static const struct rproc_ops scp_ops = {
+ .start = scp_start,
+ .stop = scp_stop,
+ .load = scp_load,
+ .da_to_va = scp_da_to_va,
+};
+
+unsigned int scp_get_vdec_hw_capa(struct platform_device *pdev)
+{
+ struct mtk_scp *scp = platform_get_drvdata(pdev);
+
+ return scp->run.dec_capability;
+}
+EXPORT_SYMBOL_GPL(scp_get_vdec_hw_capa);
+
+unsigned int scp_get_venc_hw_capa(struct platform_device *pdev)
+{
+ struct mtk_scp *scp = platform_get_drvdata(pdev);
+
+ return scp->run.enc_capability;
+}
+EXPORT_SYMBOL_GPL(scp_get_venc_hw_capa);
+
+void *scp_mapping_dm_addr(struct platform_device *pdev, u32 mem_addr)
+{
+ struct mtk_scp *scp = platform_get_drvdata(pdev);
+ void *ptr;
+
+ ptr = scp_da_to_va(scp->rproc, mem_addr, 0);
+ if (!ptr)
+ return ERR_PTR(-EINVAL);
+
+ return ptr;
+}
+EXPORT_SYMBOL_GPL(scp_mapping_dm_addr);
+
+static int scp_map_memory_region(struct mtk_scp *scp)
+{
+ struct device_node *node;
+ struct resource r;
+ int ret;
+
+ node = of_parse_phandle(scp->dev->of_node, "memory-region", 0);
+ if (!node) {
+ dev_err(scp->dev, "no memory-region specified\n");
+ return -EINVAL;
+ }
+
+ ret = of_address_to_resource(node, 0, &r);
+ if (ret)
+ return ret;
+
+ scp->phys_addr = r.start;
+ scp->dram_size = resource_size(&r);
+ scp->cpu_addr =
+ devm_ioremap_wc(scp->dev, scp->phys_addr, scp->dram_size);
+
+ if (!scp->cpu_addr) {
+ dev_err(scp->dev, "unable to map memory region: %pa+%zx\n",
+ &r.start, scp->dram_size);
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static int scp_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct mtk_scp *scp;
+ struct rproc *rproc;
+ struct resource *res;
+ char *fw_name = "scp.img";
+ int ret;
+
+ rproc = rproc_alloc(dev,
+ np->name,
+ &scp_ops,
+ fw_name,
+ sizeof(*scp));
+ if (!rproc) {
+ dev_err(dev, "unable to allocate remoteproc\n");
+ return -ENOMEM;
+ }
+
+ scp = (struct mtk_scp *)rproc->priv;
+ scp->rproc = rproc;
+ scp->dev = dev;
+ platform_set_drvdata(pdev, scp);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sram");
+ scp->sram_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR((__force void *)scp->sram_base)) {
+ dev_err(dev, "Failed to parse and map sram memory\n");
+ ret = PTR_ERR((__force void *)scp->sram_base);
+ goto free_rproc;
+ }
+ scp->sram_size = resource_size(res);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg");
+ scp->reg_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR((__force void *)scp->reg_base)) {
+ dev_err(dev, "Failed to parse and map cfg memory\n");
+ ret = PTR_ERR((__force void *)scp->reg_base);
+ goto free_rproc;
+ }
+
+ ret = scp_map_memory_region(scp);
+ if (ret)
+ goto free_rproc;
+
+ scp->clk = devm_clk_get(dev, "main");
+ if (IS_ERR(scp->clk)) {
+ dev_err(dev, "Failed to get clock\n");
+ ret = PTR_ERR(scp->clk);
+ goto free_rproc;
+ }
+
+ ret = clk_prepare_enable(scp->clk);
+ if (ret) {
+ dev_err(dev, "failed to enable clocks\n");
+ goto free_rproc;
+ }
+
+ ret = scp_ipi_init(scp);
+ clk_disable_unprepare(scp->clk);
+ if (ret) {
+ dev_err(dev, "Failed to init ipi\n");
+ goto free_rproc;
+ }
+
+ /* register SCP initialization IPI */
+ ret = scp_ipi_register(pdev,
+ SCP_IPI_INIT,
+ scp_init_ipi_handler,
+ scp);
+ if (ret) {
+ dev_err(dev, "Failed to register IPI_SCP_INIT\n");
+ goto free_rproc;
+ }
+
+ mutex_init(&scp->send_lock);
+ mutex_init(&scp->desc_lock);
+
+ init_waitqueue_head(&scp->run.wq);
+ init_waitqueue_head(&scp->ack_wq);
+
+ ret = devm_request_threaded_irq(dev, platform_get_irq(pdev, 0), NULL,
+ scp_irq_handler, IRQF_ONESHOT,
+ pdev->name, scp);
+
+ if (ret) {
+ dev_err(dev, "failed to request irq\n");
+ goto destroy_mutex;
+ }
+
+ ret = rproc_add(rproc);
+ if (ret)
+ goto destroy_mutex;
+
+ return ret;
+
+destroy_mutex:
+ mutex_destroy(&scp->desc_lock);
+ mutex_destroy(&scp->send_lock);
+free_rproc:
+ rproc_free(rproc);
+
+ return ret;
+}
+
+static int scp_remove(struct platform_device *pdev)
+{
+ struct mtk_scp *scp = platform_get_drvdata(pdev);
+
+ mutex_destroy(&scp->desc_lock);
+ mutex_destroy(&scp->send_lock);
+ rproc_del(scp->rproc);
+ rproc_free(scp->rproc);
+
+ return 0;
+}
+
+static const struct of_device_id mtk_scp_of_match[] = {
+ { .compatible = "mediatek,mt8183-scp"},
+ {},
+};
+MODULE_DEVICE_TABLE(of, mtk_scp_of_match);
+
+static struct platform_driver mtk_scp_driver = {
+ .probe = scp_probe,
+ .remove = scp_remove,
+ .driver = {
+ .name = "mtk-scp",
+ .of_match_table = of_match_ptr(mtk_scp_of_match),
+ },
+};
+
+module_platform_driver(mtk_scp_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MediaTek SCP control driver");
diff --git a/drivers/remoteproc/mtk_scp_ipi.c b/drivers/remoteproc/mtk_scp_ipi.c
new file mode 100644
index 000000000000..1bff5567df8e
--- /dev/null
+++ b/drivers/remoteproc/mtk_scp_ipi.c
@@ -0,0 +1,160 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (c) 2019 MediaTek Inc.
+
+#include <asm/barrier.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/remoteproc/mtk_scp.h>
+
+#include "mtk_common.h"
+
+int scp_ipi_register(struct platform_device *pdev,
+ enum scp_ipi_id id,
+ scp_ipi_handler_t handler,
+ void *priv)
+{
+ struct mtk_scp *scp = platform_get_drvdata(pdev);
+
+ if (!scp) {
+ dev_err(&pdev->dev, "scp device is not ready\n");
+ return -EPROBE_DEFER;
+ }
+
+ if (WARN_ON(id < 0) || WARN_ON(id >= SCP_IPI_MAX) ||
+ WARN_ON(handler == NULL))
+ return -EINVAL;
+
+ mutex_lock(&scp->desc_lock);
+ scp->ipi_desc[id].handler = handler;
+ scp->ipi_desc[id].priv = priv;
+ mutex_unlock(&scp->desc_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(scp_ipi_register);
+
+void scp_ipi_unregister(struct platform_device *pdev, enum scp_ipi_id id)
+{
+ struct mtk_scp *scp = platform_get_drvdata(pdev);
+
+ if (!scp)
+ return;
+
+ if (WARN_ON(id < 0) || WARN_ON(id >= SCP_IPI_MAX))
+ return;
+
+ mutex_lock(&scp->desc_lock);
+ scp->ipi_desc[id].handler = NULL;
+ scp->ipi_desc[id].priv = NULL;
+ mutex_unlock(&scp->desc_lock);
+}
+EXPORT_SYMBOL_GPL(scp_ipi_unregister);
+
+/*
+ * Copy src to dst, where dst is in SCP SRAM region.
+ * Since AP access of SCP SRAM don't support byte write, this always write a
+ * full word at a time, and may cause some extra bytes to be written at the
+ * beginning & ending of dst.
+ */
+void scp_memcpy_aligned(void *dst, const void *src, unsigned int len)
+{
+ void *ptr;
+ u32 val;
+ unsigned int i = 0;
+
+ if (!IS_ALIGNED((unsigned long)dst, 4)) {
+ ptr = (void *)ALIGN_DOWN((unsigned long)dst, 4);
+ i = 4 - (dst - ptr);
+ val = readl_relaxed(ptr);
+ memcpy((u8 *)&val + (4 - i), src, i);
+ writel_relaxed(val, ptr);
+ }
+
+ while (i + 4 <= len) {
+ val = *((u32 *)(src + i));
+ writel_relaxed(val, dst + i);
+ i += 4;
+ }
+ if (i < len) {
+ val = readl_relaxed(dst + i);
+ memcpy(&val, src + i, len - i);
+ writel_relaxed(val, dst + i);
+ }
+}
+EXPORT_SYMBOL_GPL(scp_memcpy_aligned);
+
+int scp_ipi_send(struct platform_device *pdev,
+ enum scp_ipi_id id,
+ void *buf,
+ unsigned int len,
+ unsigned int wait)
+{
+ struct mtk_scp *scp = platform_get_drvdata(pdev);
+ struct share_obj *send_obj = scp->send_buf;
+ unsigned long timeout;
+ int ret;
+
+ if (WARN_ON(id <= SCP_IPI_INIT) || WARN_ON(id >= SCP_IPI_MAX) ||
+ WARN_ON(len > sizeof(send_obj->share_buf)) || WARN_ON(!buf))
+ return -EINVAL;
+
+ mutex_lock(&scp->send_lock);
+
+ ret = clk_prepare_enable(scp->clk);
+ if (ret) {
+ dev_err(scp->dev, "failed to enable clock\n");
+ goto unlock_mutex;
+ }
+
+ /* Wait until SCP receives the last command */
+ timeout = jiffies + msecs_to_jiffies(2000);
+ do {
+ if (time_after(jiffies, timeout)) {
+ dev_err(scp->dev, "%s: IPI timeout!\n", __func__);
+ ret = -ETIMEDOUT;
+ goto clock_disable;
+ }
+ } while (readl(scp->reg_base + MT8183_HOST_TO_SCP));
+
+ scp_memcpy_aligned(send_obj->share_buf, buf, len);
+
+ send_obj->len = len;
+ send_obj->id = id;
+
+ scp->ipi_id_ack[id] = false;
+ /*
+ * Ensure that all writes to SRAM are committed before sending the
+ * interrupt to SCP.
+ */
+ mb();
+ /* send the command to SCP */
+ writel(MT8183_HOST_IPC_INT_BIT, scp->reg_base + MT8183_HOST_TO_SCP);
+
+ if (wait) {
+ /* wait for SCP's ACK */
+ timeout = msecs_to_jiffies(wait);
+ ret = wait_event_timeout(scp->ack_wq,
+ scp->ipi_id_ack[id],
+ timeout);
+ scp->ipi_id_ack[id] = false;
+ if (WARN(!ret, "scp ipi %d ack time out !", id))
+ ret = -EIO;
+ else
+ ret = 0;
+ }
+
+clock_disable:
+ clk_disable_unprepare(scp->clk);
+unlock_mutex:
+ mutex_unlock(&scp->send_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(scp_ipi_send);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MediaTek scp IPI interface");
diff --git a/include/linux/remoteproc/mtk_scp.h b/include/linux/remoteproc/mtk_scp.h
new file mode 100644
index 000000000000..b80d8e3f7959
--- /dev/null
+++ b/include/linux/remoteproc/mtk_scp.h
@@ -0,0 +1,141 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 MediaTek Inc.
+ */
+
+#ifndef _MTK_SCP_H
+#define _MTK_SCP_H
+
+#include <linux/platform_device.h>
+
+typedef void (*scp_ipi_handler_t) (void *data,
+ unsigned int len,
+ void *priv);
+
+/**
+ * enum ipi_id - the id of inter-processor interrupt
+ *
+ * @SCP_IPI_INIT: The interrupt from scp is to notfiy kernel
+ * SCP initialization completed.
+ * IPI_SCP_INIT is sent from SCP when firmware is
+ * loaded. AP doesn't need to send IPI_SCP_INIT
+ * command to SCP.
+ * For other IPI below, AP should send the request
+ * to SCP to trigger the interrupt.
+ * @SCP_IPI_MAX: The maximum IPI number
+ */
+
+enum scp_ipi_id {
+ SCP_IPI_INIT = 0,
+ SCP_IPI_VDEC_H264,
+ SCP_IPI_VDEC_VP8,
+ SCP_IPI_VDEC_VP9,
+ SCP_IPI_VENC_H264,
+ SCP_IPI_VENC_VP8,
+ SCP_IPI_MDP_INIT,
+ SCP_IPI_MDP_DEINIT,
+ SCP_IPI_MDP_FRAME,
+ SCP_IPI_DIP,
+ SCP_IPI_ISP_CMD,
+ SCP_IPI_ISP_FRAME,
+ SCP_IPI_FD_CMD,
+ SCP_IPI_CROS_HOST_CMD,
+ SCP_IPI_MAX,
+};
+
+/**
+ * scp_ipi_register - register an ipi function
+ *
+ * @pdev: SCP platform device
+ * @id: IPI ID
+ * @handler: IPI handler
+ * @priv: private data for IPI handler
+ *
+ * Register an ipi function to receive ipi interrupt from SCP.
+ *
+ * Return: Return 0 if ipi registers successfully, otherwise it is failed.
+ */
+int scp_ipi_register(struct platform_device *pdev,
+ enum scp_ipi_id id,
+ scp_ipi_handler_t handler,
+ void *priv);
+
+/**
+ * scp_ipi_unregister - unregister an ipi function
+ *
+ * @pdev: SCP platform device
+ * @id: IPI ID
+ *
+ * Unregister an ipi function to receive ipi interrupt from SCP.
+ */
+void scp_ipi_unregister(struct platform_device *pdev, enum scp_ipi_id id);
+
+/**
+ * scp_ipi_send - send data from AP to scp.
+ *
+ * @pdev: SCP platform device
+ * @id: IPI ID
+ * @buf: the data buffer
+ * @len: the data buffer length
+ * @wait: 1: need ack
+ *
+ * This function is thread-safe. When this function returns,
+ * SCP has received the data and starts the processing.
+ * When the processing completes, IPI handler registered
+ * by scp_ipi_register will be called in interrupt context.
+ *
+ * Return: Return 0 if sending data successfully, otherwise it is failed.
+ **/
+int scp_ipi_send(struct platform_device *pdev,
+ enum scp_ipi_id id,
+ void *buf,
+ unsigned int len,
+ unsigned int wait);
+
+/**
+ * scp_get_pdev - get SCP's platform device
+ *
+ * @pdev: the platform device of the module requesting SCP platform
+ * device for using SCP API.
+ *
+ * Return: Return NULL if it is failed.
+ * otherwise it is SCP's platform device
+ **/
+struct platform_device *scp_get_pdev(struct platform_device *pdev);
+
+/**
+ * scp_get_vdec_hw_capa - get video decoder hardware capability
+ *
+ * @pdev: SCP platform device
+ *
+ * Return: video decoder hardware capability
+ **/
+unsigned int scp_get_vdec_hw_capa(struct platform_device *pdev);
+
+/**
+ * scp_get_venc_hw_capa - get video encoder hardware capability
+ *
+ * @pdev: SCP platform device
+ *
+ * Return: video encoder hardware capability
+ **/
+unsigned int scp_get_venc_hw_capa(struct platform_device *pdev);
+
+/**
+ * scp_mapping_dm_addr - Mapping SRAM/DRAM to kernel virtual address
+ *
+ * @pdev: SCP platform device
+ * @mem_addr: SCP views memory address
+ *
+ * Mapping the SCP's SRAM address /
+ * DMEM (Data Extended Memory) memory address /
+ * Working buffer memory address to
+ * kernel virtual address.
+ *
+ * Return: Return ERR_PTR(-EINVAL) if mapping failed,
+ * otherwise the mapped kernel virtual address
+ **/
+void *scp_mapping_dm_addr(struct platform_device *pdev,
+ u32 mem_addr);
+
+#endif /* _MTK_SCP_H */
--
2.23.0.rc1.153.gdeed80330f-goog
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v16 3/5] remoteproc: mt8183: add reserved memory manager API
From: Pi-Hsun Shih @ 2019-08-15 7:16 UTC (permalink / raw)
Cc: Ohad Ben-Cohen, Erin Lo,
open list:REMOTE PROCESSOR REMOTEPROC SUBSYSTEM, open list,
Bjorn Andersson, moderated list:ARM/Mediatek SoC support,
Pi-Hsun Shih, Matthias Brugger,
moderated list:ARM/Mediatek SoC support
In-Reply-To: <20190815071635.168671-1-pihsun@chromium.org>
From: Erin Lo <erin.lo@mediatek.com>
Add memory table mapping API for other driver to lookup
reserved physical and virtual memory
Signed-off-by: Erin Lo <erin.lo@mediatek.com>
Signed-off-by: Pi-Hsun Shih <pihsun@chromium.org>
---
Changes from v15:
- No change.
Changes from v14:
- Fix a typo in variable name in DEBUG section.
Changes from v13:
- Add one more reserved region.
- Rename scp_get_reserve_* to scp_get_reserved_*.
- Minor fixes addressing comment.
Changes from v12:
- Reformat a line to fit 80 character width.
Changes from v11:
- No change.
Changes from v10:
- Fix some type mismatch warnings when printing debug messages.
Changes from v9:
- No change.
Changes from v8:
- Add more reserved regions for camera ISP.
Changes from v7, v6, v5:
- No change.
Changes from v4:
- New patch.
---
drivers/remoteproc/mtk_scp.c | 145 +++++++++++++++++++++++++++++
include/linux/remoteproc/mtk_scp.h | 25 +++++
2 files changed, 170 insertions(+)
diff --git a/drivers/remoteproc/mtk_scp.c b/drivers/remoteproc/mtk_scp.c
index 2e4fa3e41f84..ae820a35bd52 100644
--- a/drivers/remoteproc/mtk_scp.c
+++ b/drivers/remoteproc/mtk_scp.c
@@ -366,11 +366,142 @@ void *scp_mapping_dm_addr(struct platform_device *pdev, u32 mem_addr)
}
EXPORT_SYMBOL_GPL(scp_mapping_dm_addr);
+#if SCP_RESERVED_MEM
+static phys_addr_t scp_mem_base_phys;
+static phys_addr_t scp_mem_base_virt;
+static size_t scp_mem_size;
+
+static struct scp_reserve_mblock scp_reserve_mblock[] = {
+ {
+ .num = SCP_ISP_MEM_ID,
+ .start_phys = 0x0,
+ .start_virt = 0x0,
+ .size = 0x200000, /*2MB*/
+ },
+ {
+ .num = SCP_ISP_MEM2_ID,
+ .start_phys = 0x0,
+ .start_virt = 0x0,
+ .size = 0x800000, /*8MB*/
+ },
+ {
+ .num = SCP_MDP_MEM_ID,
+ .start_phys = 0x0,
+ .start_virt = 0x0,
+ .size = 0x600000, /*6MB*/
+ },
+ {
+ .num = SCP_DIP_MEM_ID,
+ .start_phys = 0x0,
+ .start_virt = 0x0,
+ .size = 0x900000, /*9MB*/
+ },
+ {
+ .num = SCP_FD_MEM_ID,
+ .start_phys = 0x0,
+ .start_virt = 0x0,
+ .size = 0x100000, /*1MB*/
+ },
+ {
+ .num = SCP_FD_MEM2_ID,
+ .start_phys = 0x0,
+ .start_virt = 0x0,
+ .size = 0x100000, /*1MB*/
+ },
+};
+
+static int scp_reserve_mem_init(struct mtk_scp *scp)
+{
+ enum scp_reserve_mem_id_t id;
+ phys_addr_t accumlate_memory_size = 0;
+
+ scp_mem_base_phys = (phys_addr_t) (scp->phys_addr + MAX_CODE_SIZE);
+ scp_mem_size = scp->dram_size - MAX_CODE_SIZE;
+
+ dev_info(scp->dev,
+ "phys:0x%llx - 0x%llx (0x%llx)\n",
+ (unsigned long long)scp_mem_base_phys,
+ (unsigned long long)(scp_mem_base_phys + scp_mem_size),
+ (unsigned long long)scp_mem_size);
+ accumlate_memory_size = 0;
+ for (id = 0; id < SCP_NUMS_MEM_ID; id++) {
+ scp_reserve_mblock[id].start_phys =
+ scp_mem_base_phys + accumlate_memory_size;
+ accumlate_memory_size += scp_reserve_mblock[id].size;
+ dev_info(
+ scp->dev,
+ "[reserve_mem:%d]: phys:0x%llx - 0x%llx (0x%llx)\n", id,
+ (unsigned long long)scp_reserve_mblock[id].start_phys,
+ (unsigned long long)(scp_reserve_mblock[id].start_phys +
+ scp_reserve_mblock[id].size),
+ (unsigned long long)scp_reserve_mblock[id].size);
+ }
+ return 0;
+}
+
+static int scp_reserve_memory_ioremap(struct mtk_scp *scp)
+{
+ enum scp_reserve_mem_id_t id;
+ phys_addr_t accumlate_memory_size = 0;
+
+ scp_mem_base_virt = (phys_addr_t)(size_t)ioremap_wc(scp_mem_base_phys,
+ scp_mem_size);
+
+ dev_info(scp->dev,
+ "virt:0x%llx - 0x%llx (0x%llx)\n",
+ (unsigned long long)scp_mem_base_virt,
+ (unsigned long long)(scp_mem_base_virt + scp_mem_size),
+ (unsigned long long)scp_mem_size);
+ for (id = 0; id < SCP_NUMS_MEM_ID; id++) {
+ scp_reserve_mblock[id].start_virt =
+ scp_mem_base_virt + accumlate_memory_size;
+ accumlate_memory_size += scp_reserve_mblock[id].size;
+ }
+ /* the reserved memory should be larger then expected memory
+ * or scp_reserve_mblock does not match dts
+ */
+ WARN_ON(accumlate_memory_size > scp_mem_size);
+ return 0;
+}
+phys_addr_t scp_get_reserved_mem_phys(enum scp_reserve_mem_id_t id)
+{
+ if (id >= SCP_NUMS_MEM_ID) {
+ pr_err("[SCP] no reserve memory for %d", id);
+ return 0;
+ }
+ return scp_reserve_mblock[id].start_phys;
+}
+EXPORT_SYMBOL_GPL(scp_get_reserved_mem_phys);
+
+phys_addr_t scp_get_reserved_mem_virt(enum scp_reserve_mem_id_t id)
+{
+ if (id >= SCP_NUMS_MEM_ID) {
+ pr_err("[SCP] no reserve memory for %d", id);
+ return 0;
+ }
+ return scp_reserve_mblock[id].start_virt;
+}
+EXPORT_SYMBOL_GPL(scp_get_reserved_mem_virt);
+
+size_t scp_get_reserved_mem_size(enum scp_reserve_mem_id_t id)
+{
+ if (id >= SCP_NUMS_MEM_ID) {
+ pr_err("[SCP] no reserve memory for %d", id);
+ return 0;
+ }
+ return scp_reserve_mblock[id].size;
+}
+EXPORT_SYMBOL_GPL(scp_get_reserved_mem_size);
+#endif
+
static int scp_map_memory_region(struct mtk_scp *scp)
{
struct device_node *node;
struct resource r;
int ret;
+#ifdef DEBUG
+ enum scp_reserve_mem_id_t id;
+#endif
node = of_parse_phandle(scp->dev->of_node, "memory-region", 0);
if (!node) {
@@ -393,6 +524,20 @@ static int scp_map_memory_region(struct mtk_scp *scp)
return -EBUSY;
}
+#if SCP_RESERVED_MEM
+ scp_reserve_mem_init(scp);
+ scp_reserve_memory_ioremap(scp);
+#ifdef DEBUG
+ for (id = 0; id < SCP_NUMS_MEM_ID; id++) {
+ dev_info(scp->dev,
+ "[mem_reserve-%d] phys:0x%llx,virt:0x%llx,size:0x%llx\n",
+ id,
+ scp_get_reserved_mem_phys(id),
+ scp_get_reserved_mem_virt(id),
+ scp_get_reserved_mem_size(id));
+ }
+#endif
+#endif
return 0;
}
diff --git a/include/linux/remoteproc/mtk_scp.h b/include/linux/remoteproc/mtk_scp.h
index b80d8e3f7959..707556f6b899 100644
--- a/include/linux/remoteproc/mtk_scp.h
+++ b/include/linux/remoteproc/mtk_scp.h
@@ -138,4 +138,29 @@ unsigned int scp_get_venc_hw_capa(struct platform_device *pdev);
void *scp_mapping_dm_addr(struct platform_device *pdev,
u32 mem_addr);
+#define SCP_RESERVED_MEM (1)
+#if SCP_RESERVED_MEM
+/* scp reserve memory ID definition*/
+enum scp_reserve_mem_id_t {
+ SCP_ISP_MEM_ID,
+ SCP_ISP_MEM2_ID,
+ SCP_MDP_MEM_ID,
+ SCP_DIP_MEM_ID,
+ SCP_FD_MEM_ID,
+ SCP_FD_MEM2_ID,
+ SCP_NUMS_MEM_ID,
+};
+
+struct scp_reserve_mblock {
+ enum scp_reserve_mem_id_t num;
+ u64 start_phys;
+ u64 start_virt;
+ u64 size;
+};
+
+extern phys_addr_t scp_get_reserved_mem_phys(enum scp_reserve_mem_id_t id);
+extern phys_addr_t scp_get_reserved_mem_virt(enum scp_reserve_mem_id_t id);
+extern size_t scp_get_reserved_mem_size(enum scp_reserve_mem_id_t id);
+#endif
+
#endif /* _MTK_SCP_H */
--
2.23.0.rc1.153.gdeed80330f-goog
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v16 4/5] rpmsg: add rpmsg support for mt8183 SCP.
From: Pi-Hsun Shih @ 2019-08-15 7:16 UTC (permalink / raw)
Cc: Ohad Ben-Cohen, open list:REMOTE PROCESSOR REMOTEPROC SUBSYSTEM,
open list, Bjorn Andersson,
moderated list:ARM/Mediatek SoC support, Pi-Hsun Shih,
Matthias Brugger, moderated list:ARM/Mediatek SoC support
In-Reply-To: <20190815071635.168671-1-pihsun@chromium.org>
Add a simple rpmsg support for mt8183 SCP, that use IPI / IPC directly.
Signed-off-by: Pi-Hsun Shih <pihsun@chromium.org>
---
Changes from v15:
- No change.
Changes from v14:
- Change year on Copyright header to 2019.
Changes from v13:
- No change.
Changes from v12:
- Use strscpy instead of strncpy.
Changes from v11:
- Fix a bug that when rproc_boot fails, the ns_ept won't be properly
destroyed, causing memory leak.
- Add documentation for mtk_rpmsg_info.
Changes from v10, v9, v8, v7:
- No change.
Changes from v6:
- Decouple mtk_rpmsg from mtk_scp by putting all necessary informations
(name service IPI id, register/unregister/send functions) into a
struct, and pass it to the mtk_rpmsg_create_rproc_subdev function.
Changes from v5:
- CONFIG_MTK_SCP now selects CONFIG_RPMSG_MTK_SCP, and the dummy
implementation for mtk_rpmsg_{create,destroy}_rproc_subdev when
CONFIG_RPMSG_MTK_SCP is not defined is removed.
Changes from v4:
- Match and fill the device tree node to the created rpmsg subdevice,
so the rpmsg subdevice can utilize the properties and subnodes on
device tree (This is similar to what drivers/rpmsg/qcom_smd.c does).
Changes from v3:
- Change from unprepare to stop, to stop the rpmsg driver before the
rproc is stopped, avoiding problem that some rpmsg would fail after
rproc is stopped.
- Add missing spin_lock_init, and use destroy_ept instead of kref_put.
Changes from v2:
- Unregiser IPI handler on unprepare.
- Lock the channel list on operations.
- Move SCP_IPI_NS_SERVICE to 0xFF.
Changes from v1:
- Do cleanup properly in mtk_rpmsg.c, which also removes the problem of
short-lived work items.
- Fix several issues checkpatch found.
---
drivers/remoteproc/Kconfig | 1 +
drivers/remoteproc/mtk_common.h | 2 +
drivers/remoteproc/mtk_scp.c | 38 ++-
drivers/remoteproc/mtk_scp_ipi.c | 1 +
drivers/rpmsg/Kconfig | 9 +
drivers/rpmsg/Makefile | 1 +
drivers/rpmsg/mtk_rpmsg.c | 414 +++++++++++++++++++++++++++++
include/linux/remoteproc/mtk_scp.h | 4 +-
include/linux/rpmsg/mtk_rpmsg.h | 38 +++
9 files changed, 503 insertions(+), 5 deletions(-)
create mode 100644 drivers/rpmsg/mtk_rpmsg.c
create mode 100644 include/linux/rpmsg/mtk_rpmsg.h
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index ea71cad399f7..cff3a9fa817b 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -26,6 +26,7 @@ config IMX_REMOTEPROC
config MTK_SCP
tristate "Mediatek SCP support"
depends on ARCH_MEDIATEK
+ select RPMSG_MTK_SCP
help
Say y here to support Mediatek's System Companion Processor (SCP) via
the remote processor framework.
diff --git a/drivers/remoteproc/mtk_common.h b/drivers/remoteproc/mtk_common.h
index e213039543ec..dbf9b339a0b7 100644
--- a/drivers/remoteproc/mtk_common.h
+++ b/drivers/remoteproc/mtk_common.h
@@ -69,6 +69,8 @@ struct mtk_scp {
void __iomem *cpu_addr;
phys_addr_t phys_addr;
size_t dram_size;
+
+ struct rproc_subdev *rpmsg_subdev;
};
/**
diff --git a/drivers/remoteproc/mtk_scp.c b/drivers/remoteproc/mtk_scp.c
index ae820a35bd52..33d98903c195 100644
--- a/drivers/remoteproc/mtk_scp.c
+++ b/drivers/remoteproc/mtk_scp.c
@@ -13,6 +13,7 @@
#include <linux/platform_device.h>
#include <linux/remoteproc.h>
#include <linux/remoteproc/mtk_scp.h>
+#include <linux/rpmsg/mtk_rpmsg.h>
#include "mtk_common.h"
#include "remoteproc_internal.h"
@@ -541,6 +542,31 @@ static int scp_map_memory_region(struct mtk_scp *scp)
return 0;
}
+static struct mtk_rpmsg_info mtk_scp_rpmsg_info = {
+ .send_ipi = scp_ipi_send,
+ .register_ipi = scp_ipi_register,
+ .unregister_ipi = scp_ipi_unregister,
+ .ns_ipi_id = SCP_IPI_NS_SERVICE,
+};
+
+static void scp_add_rpmsg_subdev(struct mtk_scp *scp)
+{
+ scp->rpmsg_subdev =
+ mtk_rpmsg_create_rproc_subdev(to_platform_device(scp->dev),
+ &mtk_scp_rpmsg_info);
+ if (scp->rpmsg_subdev)
+ rproc_add_subdev(scp->rproc, scp->rpmsg_subdev);
+}
+
+static void scp_remove_rpmsg_subdev(struct mtk_scp *scp)
+{
+ if (scp->rpmsg_subdev) {
+ rproc_remove_subdev(scp->rproc, scp->rpmsg_subdev);
+ mtk_rpmsg_destroy_rproc_subdev(scp->rpmsg_subdev);
+ scp->rpmsg_subdev = NULL;
+ }
+}
+
static int scp_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -623,22 +649,25 @@ static int scp_probe(struct platform_device *pdev)
init_waitqueue_head(&scp->run.wq);
init_waitqueue_head(&scp->ack_wq);
+ scp_add_rpmsg_subdev(scp);
+
ret = devm_request_threaded_irq(dev, platform_get_irq(pdev, 0), NULL,
scp_irq_handler, IRQF_ONESHOT,
pdev->name, scp);
if (ret) {
dev_err(dev, "failed to request irq\n");
- goto destroy_mutex;
+ goto remove_subdev;
}
ret = rproc_add(rproc);
if (ret)
- goto destroy_mutex;
+ goto remove_subdev;
- return ret;
+ return 0;
-destroy_mutex:
+remove_subdev:
+ scp_remove_rpmsg_subdev(scp);
mutex_destroy(&scp->desc_lock);
mutex_destroy(&scp->send_lock);
free_rproc:
@@ -651,6 +680,7 @@ static int scp_remove(struct platform_device *pdev)
{
struct mtk_scp *scp = platform_get_drvdata(pdev);
+ scp_remove_rpmsg_subdev(scp);
mutex_destroy(&scp->desc_lock);
mutex_destroy(&scp->send_lock);
rproc_del(scp->rproc);
diff --git a/drivers/remoteproc/mtk_scp_ipi.c b/drivers/remoteproc/mtk_scp_ipi.c
index 1bff5567df8e..7d59491bec9c 100644
--- a/drivers/remoteproc/mtk_scp_ipi.c
+++ b/drivers/remoteproc/mtk_scp_ipi.c
@@ -99,6 +99,7 @@ int scp_ipi_send(struct platform_device *pdev,
int ret;
if (WARN_ON(id <= SCP_IPI_INIT) || WARN_ON(id >= SCP_IPI_MAX) ||
+ WARN_ON(id == SCP_IPI_NS_SERVICE) ||
WARN_ON(len > sizeof(send_obj->share_buf)) || WARN_ON(!buf))
return -EINVAL;
diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig
index d0322b41eca5..85e3cc075cb4 100644
--- a/drivers/rpmsg/Kconfig
+++ b/drivers/rpmsg/Kconfig
@@ -15,6 +15,15 @@ config RPMSG_CHAR
in /dev. They make it possible for user-space programs to send and
receive rpmsg packets.
+config RPMSG_MTK_SCP
+ tristate "MediaTek SCP"
+ depends on MTK_SCP
+ select RPMSG
+ help
+ Say y here to enable support providing communication channels to
+ remote processors in MediaTek platforms.
+ This use IPI and IPC to communicate with remote processors.
+
config RPMSG_QCOM_GLINK_NATIVE
tristate
select RPMSG
diff --git a/drivers/rpmsg/Makefile b/drivers/rpmsg/Makefile
index 9aa859502d27..ae92a7fb08f6 100644
--- a/drivers/rpmsg/Makefile
+++ b/drivers/rpmsg/Makefile
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_RPMSG) += rpmsg_core.o
obj-$(CONFIG_RPMSG_CHAR) += rpmsg_char.o
+obj-$(CONFIG_RPMSG_MTK_SCP) += mtk_rpmsg.o
obj-$(CONFIG_RPMSG_QCOM_GLINK_RPM) += qcom_glink_rpm.o
obj-$(CONFIG_RPMSG_QCOM_GLINK_NATIVE) += qcom_glink_native.o
obj-$(CONFIG_RPMSG_QCOM_GLINK_SMEM) += qcom_glink_smem.o
diff --git a/drivers/rpmsg/mtk_rpmsg.c b/drivers/rpmsg/mtk_rpmsg.c
new file mode 100644
index 000000000000..ebe52c302734
--- /dev/null
+++ b/drivers/rpmsg/mtk_rpmsg.c
@@ -0,0 +1,414 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright 2019 Google LLC.
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/remoteproc.h>
+#include <linux/rpmsg/mtk_rpmsg.h>
+#include <linux/workqueue.h>
+
+#include "rpmsg_internal.h"
+
+struct mtk_rpmsg_rproc_subdev {
+ struct platform_device *pdev;
+ struct mtk_rpmsg_info *info;
+ struct rpmsg_endpoint *ns_ept;
+ struct rproc_subdev subdev;
+
+ struct work_struct register_work;
+ struct list_head channels;
+ struct mutex channels_lock;
+};
+
+#define to_mtk_subdev(d) container_of(d, struct mtk_rpmsg_rproc_subdev, subdev)
+
+struct mtk_rpmsg_channel_info {
+ struct rpmsg_channel_info info;
+ bool registered;
+ struct list_head list;
+};
+
+/**
+ * struct rpmsg_ns_msg - dynamic name service announcement message
+ * @name: name of remote service that is published
+ * @addr: address of remote service that is published
+ *
+ * This message is sent across to publish a new service. When we receive these
+ * messages, an appropriate rpmsg channel (i.e device) is created. In turn, the
+ * ->probe() handler of the appropriate rpmsg driver will be invoked
+ * (if/as-soon-as one is registered).
+ */
+struct rpmsg_ns_msg {
+ char name[RPMSG_NAME_SIZE];
+ u32 addr;
+} __packed;
+
+struct mtk_rpmsg_device {
+ struct rpmsg_device rpdev;
+ struct mtk_rpmsg_rproc_subdev *mtk_subdev;
+};
+
+struct mtk_rpmsg_endpoint {
+ struct rpmsg_endpoint ept;
+ struct mtk_rpmsg_rproc_subdev *mtk_subdev;
+};
+
+#define to_mtk_rpmsg_device(r) container_of(r, struct mtk_rpmsg_device, rpdev)
+#define to_mtk_rpmsg_endpoint(r) container_of(r, struct mtk_rpmsg_endpoint, ept)
+
+static const struct rpmsg_endpoint_ops mtk_rpmsg_endpoint_ops;
+
+static void __ept_release(struct kref *kref)
+{
+ struct rpmsg_endpoint *ept = container_of(kref, struct rpmsg_endpoint,
+ refcount);
+ kfree(to_mtk_rpmsg_endpoint(ept));
+}
+
+static void mtk_rpmsg_ipi_handler(void *data, unsigned int len, void *priv)
+{
+ struct mtk_rpmsg_endpoint *mept = priv;
+ struct rpmsg_endpoint *ept = &mept->ept;
+ int ret;
+
+ ret = (*ept->cb)(ept->rpdev, data, len, ept->priv, ept->addr);
+ if (ret)
+ dev_warn(&ept->rpdev->dev, "rpmsg handler return error = %d",
+ ret);
+}
+
+static struct rpmsg_endpoint *
+__rpmsg_create_ept(struct mtk_rpmsg_rproc_subdev *mtk_subdev,
+ struct rpmsg_device *rpdev, rpmsg_rx_cb_t cb, void *priv,
+ u32 id)
+{
+ struct mtk_rpmsg_endpoint *mept;
+ struct rpmsg_endpoint *ept;
+ struct platform_device *pdev = mtk_subdev->pdev;
+ int ret;
+
+ mept = kzalloc(sizeof(*mept), GFP_KERNEL);
+ if (!mept)
+ return NULL;
+ mept->mtk_subdev = mtk_subdev;
+
+ ept = &mept->ept;
+ kref_init(&ept->refcount);
+
+ ept->rpdev = rpdev;
+ ept->cb = cb;
+ ept->priv = priv;
+ ept->ops = &mtk_rpmsg_endpoint_ops;
+ ept->addr = id;
+
+ ret = mtk_subdev->info->register_ipi(pdev, id, mtk_rpmsg_ipi_handler,
+ mept);
+ if (ret) {
+ dev_err(&pdev->dev, "IPI register failed, id = %d", id);
+ kref_put(&ept->refcount, __ept_release);
+ return NULL;
+ }
+
+ return ept;
+}
+
+static struct rpmsg_endpoint *
+mtk_rpmsg_create_ept(struct rpmsg_device *rpdev, rpmsg_rx_cb_t cb, void *priv,
+ struct rpmsg_channel_info chinfo)
+{
+ struct mtk_rpmsg_rproc_subdev *mtk_subdev =
+ to_mtk_rpmsg_device(rpdev)->mtk_subdev;
+
+ return __rpmsg_create_ept(mtk_subdev, rpdev, cb, priv, chinfo.src);
+}
+
+static void mtk_rpmsg_destroy_ept(struct rpmsg_endpoint *ept)
+{
+ struct mtk_rpmsg_rproc_subdev *mtk_subdev =
+ to_mtk_rpmsg_endpoint(ept)->mtk_subdev;
+
+ mtk_subdev->info->unregister_ipi(mtk_subdev->pdev, ept->addr);
+ kref_put(&ept->refcount, __ept_release);
+}
+
+static int mtk_rpmsg_send(struct rpmsg_endpoint *ept, void *data, int len)
+{
+ struct mtk_rpmsg_rproc_subdev *mtk_subdev =
+ to_mtk_rpmsg_endpoint(ept)->mtk_subdev;
+
+ return mtk_subdev->info->send_ipi(mtk_subdev->pdev, ept->addr, data,
+ len, 0);
+}
+
+static int mtk_rpmsg_trysend(struct rpmsg_endpoint *ept, void *data, int len)
+{
+ struct mtk_rpmsg_rproc_subdev *mtk_subdev =
+ to_mtk_rpmsg_endpoint(ept)->mtk_subdev;
+
+ /*
+ * TODO: This currently is same as mtk_rpmsg_send, and wait until SCP
+ * received the last command.
+ */
+ return mtk_subdev->info->send_ipi(mtk_subdev->pdev, ept->addr, data,
+ len, 0);
+}
+
+static const struct rpmsg_endpoint_ops mtk_rpmsg_endpoint_ops = {
+ .destroy_ept = mtk_rpmsg_destroy_ept,
+ .send = mtk_rpmsg_send,
+ .trysend = mtk_rpmsg_trysend,
+};
+
+static void mtk_rpmsg_release_device(struct device *dev)
+{
+ struct rpmsg_device *rpdev = to_rpmsg_device(dev);
+ struct mtk_rpmsg_device *mdev = to_mtk_rpmsg_device(rpdev);
+
+ kfree(mdev);
+}
+
+static const struct rpmsg_device_ops mtk_rpmsg_device_ops = {
+ .create_ept = mtk_rpmsg_create_ept,
+};
+
+static struct device_node *
+mtk_rpmsg_match_device_subnode(struct device_node *node, const char *channel)
+{
+ struct device_node *child;
+ const char *name;
+ int ret;
+
+ for_each_available_child_of_node(node, child) {
+ ret = of_property_read_string(child, "mtk,rpmsg-name", &name);
+ if (ret)
+ continue;
+
+ if (strcmp(name, channel) == 0)
+ return child;
+ }
+
+ return NULL;
+}
+
+static int mtk_rpmsg_register_device(struct mtk_rpmsg_rproc_subdev *mtk_subdev,
+ struct rpmsg_channel_info *info)
+{
+ struct rpmsg_device *rpdev;
+ struct mtk_rpmsg_device *mdev;
+ struct platform_device *pdev = mtk_subdev->pdev;
+ int ret;
+
+ mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
+ if (!mdev)
+ return -ENOMEM;
+
+ mdev->mtk_subdev = mtk_subdev;
+
+ rpdev = &mdev->rpdev;
+ rpdev->ops = &mtk_rpmsg_device_ops;
+ rpdev->src = info->src;
+ rpdev->dst = info->dst;
+ strscpy(rpdev->id.name, info->name, RPMSG_NAME_SIZE);
+
+ rpdev->dev.of_node =
+ mtk_rpmsg_match_device_subnode(pdev->dev.of_node, info->name);
+ rpdev->dev.parent = &pdev->dev;
+ rpdev->dev.release = mtk_rpmsg_release_device;
+
+ ret = rpmsg_register_device(rpdev);
+ if (ret) {
+ kfree(mdev);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void mtk_register_device_work_function(struct work_struct *register_work)
+{
+ struct mtk_rpmsg_rproc_subdev *subdev = container_of(
+ register_work, struct mtk_rpmsg_rproc_subdev, register_work);
+ struct platform_device *pdev = subdev->pdev;
+ struct mtk_rpmsg_channel_info *info;
+ int ret;
+
+ mutex_lock(&subdev->channels_lock);
+ list_for_each_entry(info, &subdev->channels, list) {
+ if (info->registered)
+ continue;
+
+ ret = mtk_rpmsg_register_device(subdev, &info->info);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't create rpmsg_device\n");
+ continue;
+ }
+
+ info->registered = true;
+ }
+ mutex_unlock(&subdev->channels_lock);
+}
+
+static int mtk_rpmsg_create_device(struct mtk_rpmsg_rproc_subdev *mtk_subdev,
+ char *name, u32 addr)
+{
+ struct mtk_rpmsg_channel_info *info;
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ strscpy(info->info.name, name, RPMSG_NAME_SIZE);
+ info->info.src = addr;
+ info->info.dst = RPMSG_ADDR_ANY;
+ mutex_lock(&mtk_subdev->channels_lock);
+ list_add(&info->list, &mtk_subdev->channels);
+ mutex_unlock(&mtk_subdev->channels_lock);
+
+ schedule_work(&mtk_subdev->register_work);
+ return 0;
+}
+
+static int mtk_rpmsg_ns_cb(struct rpmsg_device *rpdev, void *data, int len,
+ void *priv, u32 src)
+{
+ struct rpmsg_ns_msg *msg = data;
+ struct mtk_rpmsg_rproc_subdev *mtk_subdev = priv;
+ struct device *dev = &mtk_subdev->pdev->dev;
+
+ int ret;
+
+ if (len != sizeof(*msg)) {
+ dev_err(dev, "malformed ns msg (%d)\n", len);
+ return -EINVAL;
+ }
+
+ /*
+ * the name service ept does _not_ belong to a real rpmsg channel,
+ * and is handled by the rpmsg bus itself.
+ * for sanity reasons, make sure a valid rpdev has _not_ sneaked
+ * in somehow.
+ */
+ if (rpdev) {
+ dev_err(dev, "anomaly: ns ept has an rpdev handle\n");
+ return -EINVAL;
+ }
+
+ /* don't trust the remote processor for null terminating the name */
+ msg->name[RPMSG_NAME_SIZE - 1] = '\0';
+
+ dev_info(dev, "creating channel %s addr 0x%x\n", msg->name, msg->addr);
+
+ ret = mtk_rpmsg_create_device(mtk_subdev, msg->name, msg->addr);
+ if (ret) {
+ dev_err(dev, "create rpmsg device failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+int mtk_rpmsg_prepare(struct rproc_subdev *subdev)
+{
+ struct mtk_rpmsg_rproc_subdev *mtk_subdev = to_mtk_subdev(subdev);
+
+ /* a dedicated endpoint handles the name service msgs */
+ if (mtk_subdev->info->ns_ipi_id >= 0) {
+ mtk_subdev->ns_ept =
+ __rpmsg_create_ept(mtk_subdev, NULL, mtk_rpmsg_ns_cb,
+ mtk_subdev,
+ mtk_subdev->info->ns_ipi_id);
+ if (!mtk_subdev->ns_ept) {
+ dev_err(&mtk_subdev->pdev->dev,
+ "failed to create name service endpoint\n");
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+
+void mtk_rpmsg_unprepare(struct rproc_subdev *subdev)
+{
+ struct mtk_rpmsg_rproc_subdev *mtk_subdev = to_mtk_subdev(subdev);
+
+ if (mtk_subdev->ns_ept) {
+ mtk_rpmsg_destroy_ept(mtk_subdev->ns_ept);
+ mtk_subdev->ns_ept = NULL;
+ }
+}
+
+void mtk_rpmsg_stop(struct rproc_subdev *subdev, bool crashed)
+{
+ struct mtk_rpmsg_channel_info *info, *next;
+ struct mtk_rpmsg_rproc_subdev *mtk_subdev = to_mtk_subdev(subdev);
+ struct device *dev = &mtk_subdev->pdev->dev;
+
+ /*
+ * Destroy the name service endpoint here, to avoid new channel being
+ * created after the rpmsg_unregister_device loop below.
+ */
+ if (mtk_subdev->ns_ept) {
+ mtk_rpmsg_destroy_ept(mtk_subdev->ns_ept);
+ mtk_subdev->ns_ept = NULL;
+ }
+
+ cancel_work_sync(&mtk_subdev->register_work);
+
+ mutex_lock(&mtk_subdev->channels_lock);
+ list_for_each_entry(info, &mtk_subdev->channels, list) {
+ if (!info->registered)
+ continue;
+ if (rpmsg_unregister_device(dev, &info->info)) {
+ dev_warn(
+ dev,
+ "rpmsg_unregister_device failed for %s.%d.%d\n",
+ info->info.name, info->info.src,
+ info->info.dst);
+ }
+ }
+
+ list_for_each_entry_safe(info, next,
+ &mtk_subdev->channels, list) {
+ list_del(&info->list);
+ kfree(info);
+ }
+ mutex_unlock(&mtk_subdev->channels_lock);
+}
+
+struct rproc_subdev *
+mtk_rpmsg_create_rproc_subdev(struct platform_device *pdev,
+ struct mtk_rpmsg_info *info)
+{
+ struct mtk_rpmsg_rproc_subdev *mtk_subdev;
+
+ mtk_subdev = kzalloc(sizeof(*mtk_subdev), GFP_KERNEL);
+ if (!mtk_subdev)
+ return NULL;
+
+ mtk_subdev->pdev = pdev;
+ mtk_subdev->subdev.prepare = mtk_rpmsg_prepare;
+ mtk_subdev->subdev.stop = mtk_rpmsg_stop;
+ mtk_subdev->subdev.unprepare = mtk_rpmsg_unprepare;
+ mtk_subdev->info = info;
+ INIT_LIST_HEAD(&mtk_subdev->channels);
+ INIT_WORK(&mtk_subdev->register_work,
+ mtk_register_device_work_function);
+ mutex_init(&mtk_subdev->channels_lock);
+
+ return &mtk_subdev->subdev;
+}
+EXPORT_SYMBOL_GPL(mtk_rpmsg_create_rproc_subdev);
+
+void mtk_rpmsg_destroy_rproc_subdev(struct rproc_subdev *subdev)
+{
+ struct mtk_rpmsg_rproc_subdev *mtk_subdev = to_mtk_subdev(subdev);
+
+ kfree(mtk_subdev);
+}
+EXPORT_SYMBOL_GPL(mtk_rpmsg_destroy_rproc_subdev);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MediaTek scp rpmsg driver");
diff --git a/include/linux/remoteproc/mtk_scp.h b/include/linux/remoteproc/mtk_scp.h
index 707556f6b899..67ae6674397b 100644
--- a/include/linux/remoteproc/mtk_scp.h
+++ b/include/linux/remoteproc/mtk_scp.h
@@ -40,9 +40,11 @@ enum scp_ipi_id {
SCP_IPI_ISP_FRAME,
SCP_IPI_FD_CMD,
SCP_IPI_CROS_HOST_CMD,
- SCP_IPI_MAX,
+ SCP_IPI_NS_SERVICE = 0xFF,
+ SCP_IPI_MAX = 0x100,
};
+
/**
* scp_ipi_register - register an ipi function
*
diff --git a/include/linux/rpmsg/mtk_rpmsg.h b/include/linux/rpmsg/mtk_rpmsg.h
new file mode 100644
index 000000000000..861c1cbea523
--- /dev/null
+++ b/include/linux/rpmsg/mtk_rpmsg.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2018 Google LLC.
+ */
+
+#ifndef __LINUX_RPMSG_MTK_RPMSG_H
+#define __LINUX_RPMSG_MTK_RPMSG_H
+
+#include <linux/device.h>
+#include <linux/remoteproc.h>
+
+typedef void (*ipi_handler_t)(void *data, unsigned int len, void *priv);
+
+/*
+ * struct mtk_rpmsg_info - IPI functions tied to the rpmsg device.
+ * @register_ipi: register IPI handler for an IPI id.
+ * @unregister_ipi: unregister IPI handler for a registered IPI id.
+ * @send_ipi: send IPI to an IPI id. wait is the timeout (in msecs) to wait
+ * until response, or 0 if there's no timeout.
+ * @ns_ipi_id: the IPI id used for name service, or -1 if name service isn't
+ * supported.
+ */
+struct mtk_rpmsg_info {
+ int (*register_ipi)(struct platform_device *pdev, u32 id,
+ ipi_handler_t handler, void *priv);
+ void (*unregister_ipi)(struct platform_device *pdev, u32 id);
+ int (*send_ipi)(struct platform_device *pdev, u32 id,
+ void *buf, unsigned int len, unsigned int wait);
+ int ns_ipi_id;
+};
+
+struct rproc_subdev *
+mtk_rpmsg_create_rproc_subdev(struct platform_device *pdev,
+ struct mtk_rpmsg_info *info);
+
+void mtk_rpmsg_destroy_rproc_subdev(struct rproc_subdev *subdev);
+
+#endif
--
2.23.0.rc1.153.gdeed80330f-goog
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v16 5/5] arm64: dts: mt8183: add scp node
From: Pi-Hsun Shih @ 2019-08-15 7:16 UTC (permalink / raw)
Cc: Mark Rutland,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
Erin Lo, open list, Rob Herring,
moderated list:ARM/Mediatek SoC support, Pi-Hsun Shih,
Matthias Brugger, Eddie Huang,
moderated list:ARM/Mediatek SoC support
In-Reply-To: <20190815071635.168671-1-pihsun@chromium.org>
From: Eddie Huang <eddie.huang@mediatek.com>
Add scp node to mt8183 and mt8183-evb
Signed-off-by: Erin Lo <erin.lo@mediatek.com>
Signed-off-by: Pi-Hsun Shih <pihsun@chromium.org>
Signed-off-by: Eddie Huang <eddie.huang@mediatek.com>
---
Changes from v15, v14:
- No change.
Changes from v13:
- Change the size of the cfg register region.
Changes from v12, v11, v10:
- No change.
Changes from v9:
- Remove extra reserve-memory-vpu_share node.
Changes from v8:
- New patch.
---
arch/arm64/boot/dts/mediatek/mt8183-evb.dts | 11 +++++++++++
arch/arm64/boot/dts/mediatek/mt8183.dtsi | 12 ++++++++++++
2 files changed, 23 insertions(+)
diff --git a/arch/arm64/boot/dts/mediatek/mt8183-evb.dts b/arch/arm64/boot/dts/mediatek/mt8183-evb.dts
index d8e555cbb5d3..e46e34ce3159 100644
--- a/arch/arm64/boot/dts/mediatek/mt8183-evb.dts
+++ b/arch/arm64/boot/dts/mediatek/mt8183-evb.dts
@@ -24,6 +24,17 @@
chosen {
stdout-path = "serial0:921600n8";
};
+
+ reserved-memory {
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+ scp_mem_reserved: scp_mem_region {
+ compatible = "shared-dma-pool";
+ reg = <0 0x50000000 0 0x2900000>;
+ no-map;
+ };
+ };
};
&auxadc {
diff --git a/arch/arm64/boot/dts/mediatek/mt8183.dtsi b/arch/arm64/boot/dts/mediatek/mt8183.dtsi
index c2749c4631bc..871754c2f477 100644
--- a/arch/arm64/boot/dts/mediatek/mt8183.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8183.dtsi
@@ -254,6 +254,18 @@
clock-names = "spi", "wrap";
};
+ scp: scp@10500000 {
+ compatible = "mediatek,mt8183-scp";
+ reg = <0 0x10500000 0 0x80000>,
+ <0 0x105c0000 0 0x19080>;
+ reg-names = "sram", "cfg";
+ interrupts = <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&infracfg CLK_INFRA_SCPSYS>;
+ clock-names = "main";
+ memory-region = <&scp_mem_reserved>;
+ status = "disabled";
+ };
+
auxadc: auxadc@11001000 {
compatible = "mediatek,mt8183-auxadc",
"mediatek,mt8173-auxadc";
--
2.23.0.rc1.153.gdeed80330f-goog
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* Re: [PATCH 7/8] parisc: don't set ARCH_NO_COHERENT_DMA_MMAP
From: Christoph Hellwig @ 2019-08-15 7:36 UTC (permalink / raw)
To: Helge Deller
Cc: linux-xtensa, Michal Simek, Vladimir Murzin, linux-parisc,
linux-sh, Takashi Iwai, Robin Murphy, x86, linux-kernel, iommu,
linux-m68k, linuxppc-dev, linux-arm-kernel, Marek Szyprowski
In-Reply-To: <20190808160005.10325-8-hch@lst.de>
Helger, or other parisc folks: can you take a look at this patch
in particular and the series in general? Thanks!
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox