Implement support for DRAM MRQ GSCs. Signed-off-by: Peter De Schrijver <pdeschrijver@xxxxxxxxxx> --- drivers/firmware/tegra/bpmp-tegra186.c | 214 +++++++++++++++++-------- drivers/firmware/tegra/bpmp.c | 4 +- 2 files changed, 153 insertions(+), 65 deletions(-) diff --git a/drivers/firmware/tegra/bpmp-tegra186.c b/drivers/firmware/tegra/bpmp-tegra186.c index 2e26199041cd..43e2563575fc 100644 --- a/drivers/firmware/tegra/bpmp-tegra186.c +++ b/drivers/firmware/tegra/bpmp-tegra186.c @@ -4,8 +4,11 @@ */ #include <linux/genalloc.h> +#include <linux/io.h> #include <linux/mailbox_client.h> +#include <linux/of_address.h> #include <linux/platform_device.h> +#include <linux/range.h> #include <soc/tegra/bpmp.h> #include <soc/tegra/bpmp-abi.h> @@ -13,12 +16,13 @@ #include "bpmp-private.h" +enum tegra_bpmp_mem_type { TEGRA_INVALID, TEGRA_SRAM, TEGRA_RMEM }; + struct tegra186_bpmp { struct tegra_bpmp *parent; struct { - struct gen_pool *pool; - void __iomem *virt; + void *virt; dma_addr_t phys; } tx, rx; @@ -26,6 +30,12 @@ struct tegra186_bpmp { struct mbox_client client; struct mbox_chan *channel; } mbox; + + struct { + struct gen_pool *tx, *rx; + } sram; + + enum tegra_bpmp_mem_type type; }; static inline struct tegra_bpmp * @@ -118,8 +128,8 @@ static int tegra186_bpmp_channel_init(struct tegra_bpmp_channel *channel, queue_size = tegra_ivc_total_queue_size(message_size); offset = queue_size * index; - iosys_map_set_vaddr_iomem(&rx, priv->rx.virt + offset); - iosys_map_set_vaddr_iomem(&tx, priv->tx.virt + offset); + iosys_map_set_vaddr_iomem(&rx, (void __iomem *)priv->rx.virt + offset); + iosys_map_set_vaddr_iomem(&tx, (void __iomem *)priv->tx.virt + offset); err = tegra_ivc_init(channel->ivc, NULL, &rx, priv->rx.phys + offset, &tx, priv->tx.phys + offset, 1, message_size, tegra186_bpmp_ivc_notify, @@ -158,64 +168,171 @@ static void mbox_handle_rx(struct mbox_client *client, void *data) tegra_bpmp_handle_rx(bpmp); } -static int tegra186_bpmp_init(struct tegra_bpmp *bpmp) +static void tegra186_bpmp_channel_deinit(struct tegra_bpmp *bpmp) +{ + int i; + struct tegra186_bpmp *priv = bpmp->priv; + + for (i = 0; i < bpmp->threaded.count; i++) { + if (!bpmp->threaded_channels[i].bpmp) + continue; + + tegra186_bpmp_channel_cleanup(&bpmp->threaded_channels[i]); + } + + tegra186_bpmp_channel_cleanup(bpmp->rx_channel); + tegra186_bpmp_channel_cleanup(bpmp->tx_channel); + + if (priv->type == TEGRA_SRAM) { + gen_pool_free(priv->sram.tx, (unsigned long)priv->tx.virt, 4096); + gen_pool_free(priv->sram.rx, (unsigned long)priv->rx.virt, 4096); + } else if (priv->type == TEGRA_RMEM) { + memunmap(priv->tx.virt); + } +} + +static int tegra186_bpmp_channel_setup(struct tegra_bpmp *bpmp) { - struct tegra186_bpmp *priv; unsigned int i; int err; - priv = devm_kzalloc(bpmp->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; + err = tegra186_bpmp_channel_init(bpmp->tx_channel, bpmp, + bpmp->soc->channels.cpu_tx.offset); + if (err < 0) + return err; - bpmp->priv = priv; - priv->parent = bpmp; + err = tegra186_bpmp_channel_init(bpmp->rx_channel, bpmp, + bpmp->soc->channels.cpu_rx.offset); + if (err < 0) { + tegra186_bpmp_channel_cleanup(bpmp->tx_channel); + return err; + } + + for (i = 0; i < bpmp->threaded.count; i++) { + unsigned int index = bpmp->soc->channels.thread.offset + i; - priv->tx.pool = of_gen_pool_get(bpmp->dev->of_node, "shmem", 0); - if (!priv->tx.pool) { + err = tegra186_bpmp_channel_init(&bpmp->threaded_channels[i], + bpmp, index); + if (err < 0) + break; + } + + if (err < 0) + tegra186_bpmp_channel_deinit(bpmp); + + return err; +} + +static void tegra186_bpmp_reset_channels(struct tegra_bpmp *bpmp) +{ + unsigned int i; + + tegra186_bpmp_channel_reset(bpmp->tx_channel); + tegra186_bpmp_channel_reset(bpmp->rx_channel); + + for (i = 0; i < bpmp->threaded.count; i++) + tegra186_bpmp_channel_reset(&bpmp->threaded_channels[i]); +} + +static int tegra186_bpmp_sram_init(struct tegra_bpmp *bpmp) +{ + int err; + struct tegra186_bpmp *priv = bpmp->priv; + + priv->sram.tx = of_gen_pool_get(bpmp->dev->of_node, "shmem", 0); + if (!priv->sram.tx) { dev_err(bpmp->dev, "TX shmem pool not found\n"); return -EPROBE_DEFER; } - priv->tx.virt = (void __iomem *)gen_pool_dma_alloc(priv->tx.pool, 4096, &priv->tx.phys); + priv->tx.virt = gen_pool_dma_alloc(priv->sram.tx, 4096, &priv->tx.phys); if (!priv->tx.virt) { dev_err(bpmp->dev, "failed to allocate from TX pool\n"); return -ENOMEM; } - priv->rx.pool = of_gen_pool_get(bpmp->dev->of_node, "shmem", 1); - if (!priv->rx.pool) { + priv->sram.rx = of_gen_pool_get(bpmp->dev->of_node, "shmem", 1); + if (!priv->sram.rx) { dev_err(bpmp->dev, "RX shmem pool not found\n"); err = -EPROBE_DEFER; goto free_tx; } - priv->rx.virt = (void __iomem *)gen_pool_dma_alloc(priv->rx.pool, 4096, &priv->rx.phys); + priv->rx.virt = gen_pool_dma_alloc(priv->sram.rx, 4096, &priv->rx.phys); if (!priv->rx.virt) { dev_err(bpmp->dev, "failed to allocate from RX pool\n"); err = -ENOMEM; goto free_tx; } - err = tegra186_bpmp_channel_init(bpmp->tx_channel, bpmp, - bpmp->soc->channels.cpu_tx.offset); - if (err < 0) - goto free_rx; + priv->type = TEGRA_SRAM; - err = tegra186_bpmp_channel_init(bpmp->rx_channel, bpmp, - bpmp->soc->channels.cpu_rx.offset); - if (err < 0) - goto cleanup_tx_channel; + return 0; - for (i = 0; i < bpmp->threaded.count; i++) { - unsigned int index = bpmp->soc->channels.thread.offset + i; +free_tx: + gen_pool_free(priv->sram.tx, (unsigned long)priv->tx.virt, 4096); - err = tegra186_bpmp_channel_init(&bpmp->threaded_channels[i], - bpmp, index); + return err; +} + +static enum tegra_bpmp_mem_type tegra186_bpmp_dram_init(struct tegra_bpmp *bpmp) +{ + int err; + struct resource res; + struct device_node *np; + struct tegra186_bpmp *priv = bpmp->priv; + + np = of_parse_phandle(bpmp->dev->of_node, "memory-region", 0); + if (!np) + return TEGRA_INVALID; + + err = of_address_to_resource(np, 0, &res); + if (err) { + dev_warn(bpmp->dev, "Parsing memory region returned: %d\n", err); + return TEGRA_INVALID; + } + + if ((res.end - res.start + 1) < 0x2000) { + dev_warn(bpmp->dev, "DRAM region less than 0x2000 bytes\n"); + return TEGRA_INVALID; + } + + priv->tx.phys = res.start; + priv->rx.phys = res.start + 0x1000; + + priv->tx.virt = memremap(priv->tx.phys, res.end - res.start + 1, MEMREMAP_WC); + if (priv->tx.virt == NULL) { + dev_warn(bpmp->dev, "DRAM region mapping failed\n"); + return TEGRA_INVALID; + } + priv->rx.virt = priv->tx.virt + 0x1000; + + return TEGRA_RMEM; +} + +static int tegra186_bpmp_init(struct tegra_bpmp *bpmp) +{ + struct tegra186_bpmp *priv; + int err; + + priv = devm_kzalloc(bpmp->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + bpmp->priv = priv; + priv->parent = bpmp; + + priv->type = tegra186_bpmp_dram_init(bpmp); + if (priv->type == TEGRA_INVALID) { + err = tegra186_bpmp_sram_init(bpmp); if (err < 0) - goto cleanup_channels; + return err; } + err = tegra186_bpmp_channel_setup(bpmp); + if (err < 0) + return err; + /* mbox registration */ priv->mbox.client.dev = bpmp->dev; priv->mbox.client.rx_callback = mbox_handle_rx; @@ -226,51 +343,22 @@ static int tegra186_bpmp_init(struct tegra_bpmp *bpmp) if (IS_ERR(priv->mbox.channel)) { err = PTR_ERR(priv->mbox.channel); dev_err(bpmp->dev, "failed to get HSP mailbox: %d\n", err); - goto cleanup_channels; + tegra186_bpmp_channel_deinit(bpmp); + return err; } - tegra186_bpmp_channel_reset(bpmp->tx_channel); - tegra186_bpmp_channel_reset(bpmp->rx_channel); - - for (i = 0; i < bpmp->threaded.count; i++) - tegra186_bpmp_channel_reset(&bpmp->threaded_channels[i]); + tegra186_bpmp_reset_channels(bpmp); return 0; - -cleanup_channels: - for (i = 0; i < bpmp->threaded.count; i++) { - if (!bpmp->threaded_channels[i].bpmp) - continue; - - tegra186_bpmp_channel_cleanup(&bpmp->threaded_channels[i]); - } - - tegra186_bpmp_channel_cleanup(bpmp->rx_channel); -cleanup_tx_channel: - tegra186_bpmp_channel_cleanup(bpmp->tx_channel); -free_rx: - gen_pool_free(priv->rx.pool, (unsigned long)priv->rx.virt, 4096); -free_tx: - gen_pool_free(priv->tx.pool, (unsigned long)priv->tx.virt, 4096); - - return err; } static void tegra186_bpmp_deinit(struct tegra_bpmp *bpmp) { struct tegra186_bpmp *priv = bpmp->priv; - unsigned int i; mbox_free_channel(priv->mbox.channel); - for (i = 0; i < bpmp->threaded.count; i++) - tegra186_bpmp_channel_cleanup(&bpmp->threaded_channels[i]); - - tegra186_bpmp_channel_cleanup(bpmp->rx_channel); - tegra186_bpmp_channel_cleanup(bpmp->tx_channel); - - gen_pool_free(priv->rx.pool, (unsigned long)priv->rx.virt, 4096); - gen_pool_free(priv->tx.pool, (unsigned long)priv->tx.virt, 4096); + tegra186_bpmp_channel_deinit(bpmp); } static int tegra186_bpmp_resume(struct tegra_bpmp *bpmp) diff --git a/drivers/firmware/tegra/bpmp.c b/drivers/firmware/tegra/bpmp.c index 8b5e5daa9fae..17bd3590aaa2 100644 --- a/drivers/firmware/tegra/bpmp.c +++ b/drivers/firmware/tegra/bpmp.c @@ -735,6 +735,8 @@ static int tegra_bpmp_probe(struct platform_device *pdev) if (!bpmp->threaded_channels) return -ENOMEM; + platform_set_drvdata(pdev, bpmp); + err = bpmp->soc->ops->init(bpmp); if (err < 0) return err; @@ -758,8 +760,6 @@ static int tegra_bpmp_probe(struct platform_device *pdev) dev_info(&pdev->dev, "firmware: %.*s\n", (int)sizeof(tag), tag); - platform_set_drvdata(pdev, bpmp); - err = of_platform_default_populate(pdev->dev.of_node, NULL, &pdev->dev); if (err < 0) goto free_mrq; -- 2.34.1