From 0f67e2395be44db2c1bef17b6ada2e46221908ed Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 17 Jun 2016 09:43:57 -0600 Subject: mailbox: add Tegra186 HSP driver Tegra186's HSP module implements doorbells, mailboxes, semaphores, and shared interrupts. This patch provides a driver for HSP, and hooks it into the mailbox API. Currently, only doorbells are supported. Signed-off-by: Stephen Warren Reviewed-by: Simon Glass Acked-by: Simon Glass --- drivers/mailbox/Kconfig | 7 ++ drivers/mailbox/Makefile | 1 + drivers/mailbox/tegra-hsp.c | 163 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 171 insertions(+) create mode 100644 drivers/mailbox/tegra-hsp.c (limited to 'drivers/mailbox') diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index 9087512390..9649b70589 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -17,4 +17,11 @@ config SANDBOX_MBOX Enable support for a test mailbox implementation, which simply echos back a modified version of any message that is sent. +config TEGRA_HSP + bool "Enable Tegra HSP controller support" + depends on DM_MAILBOX && TEGRA + help + This enables support for the NVIDIA Tegra HSP Hw module, which + implements doorbells, mailboxes, semaphores, and shared interrupts. + endmenu diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile index bbae4def6d..155dbeb099 100644 --- a/drivers/mailbox/Makefile +++ b/drivers/mailbox/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_DM_MAILBOX) += mailbox-uclass.o obj-$(CONFIG_SANDBOX_MBOX) += sandbox-mbox.o obj-$(CONFIG_SANDBOX_MBOX) += sandbox-mbox-test.o +obj-$(CONFIG_TEGRA_HSP) += tegra-hsp.o diff --git a/drivers/mailbox/tegra-hsp.c b/drivers/mailbox/tegra-hsp.c new file mode 100644 index 0000000000..5c781a50b6 --- /dev/null +++ b/drivers/mailbox/tegra-hsp.c @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include +#include +#include +#include +#include + +#define TEGRA_HSP_DB_REG_TRIGGER 0x0 +#define TEGRA_HSP_DB_REG_ENABLE 0x4 +#define TEGRA_HSP_DB_REG_RAW 0x8 +#define TEGRA_HSP_DB_REG_PENDING 0xc + +#define TEGRA_HSP_DB_ID_CCPLEX 1 +#define TEGRA_HSP_DB_ID_BPMP 3 +#define TEGRA_HSP_DB_ID_NUM 7 + +struct tegra_hsp { + fdt_addr_t regs; + uint32_t db_base; +}; + +DECLARE_GLOBAL_DATA_PTR; + +static uint32_t *tegra_hsp_reg(struct tegra_hsp *thsp, uint32_t db_id, + uint32_t reg) +{ + return (uint32_t *)(thsp->regs + thsp->db_base + (db_id * 0x100) + reg); +} + +static uint32_t tegra_hsp_readl(struct tegra_hsp *thsp, uint32_t db_id, + uint32_t reg) +{ + uint32_t *r = tegra_hsp_reg(thsp, db_id, reg); + return readl(r); +} + +static void tegra_hsp_writel(struct tegra_hsp *thsp, uint32_t val, + uint32_t db_id, uint32_t reg) +{ + uint32_t *r = tegra_hsp_reg(thsp, db_id, reg); + + writel(val, r); + readl(r); +} + +static int tegra_hsp_db_id(ulong chan_id) +{ + switch (chan_id) { + case TEGRA_HSP_MASTER_BPMP: + return TEGRA_HSP_DB_ID_BPMP; + default: + debug("Invalid channel ID\n"); + return -EINVAL; + } +} + +static int tegra_hsp_request(struct mbox_chan *chan) +{ + int db_id; + + debug("%s(chan=%p)\n", __func__, chan); + + db_id = tegra_hsp_db_id(chan->id); + if (db_id < 0) { + debug("tegra_hsp_db_id() failed: %d\n", db_id); + return -EINVAL; + } + + return 0; +} + +static int tegra_hsp_free(struct mbox_chan *chan) +{ + debug("%s(chan=%p)\n", __func__, chan); + + return 0; +} + +static int tegra_hsp_send(struct mbox_chan *chan, const void *data) +{ + struct tegra_hsp *thsp = dev_get_priv(chan->dev); + int db_id; + + debug("%s(chan=%p, data=%p)\n", __func__, chan, data); + + db_id = tegra_hsp_db_id(chan->id); + tegra_hsp_writel(thsp, 1, db_id, TEGRA_HSP_DB_REG_TRIGGER); + + return 0; +} + +static int tegra_hsp_recv(struct mbox_chan *chan, void *data) +{ + struct tegra_hsp *thsp = dev_get_priv(chan->dev); + uint32_t db_id = TEGRA_HSP_DB_ID_CCPLEX; + uint32_t val; + + debug("%s(chan=%p, data=%p)\n", __func__, chan, data); + + val = tegra_hsp_readl(thsp, db_id, TEGRA_HSP_DB_REG_RAW); + if (!(val & BIT(chan->id))) + return -ENODATA; + + tegra_hsp_writel(thsp, BIT(chan->id), db_id, TEGRA_HSP_DB_REG_RAW); + + return 0; +} + +static int tegra_hsp_bind(struct udevice *dev) +{ + debug("%s(dev=%p)\n", __func__, dev); + + return 0; +} + +static int tegra_hsp_probe(struct udevice *dev) +{ + struct tegra_hsp *thsp = dev_get_priv(dev); + int nr_sm, nr_ss, nr_as; + + debug("%s(dev=%p)\n", __func__, dev); + + thsp->regs = dev_get_addr(dev); + if (thsp->regs == FDT_ADDR_T_NONE) + return -ENODEV; + + nr_sm = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "nvidia,num-SM", + 0); + nr_ss = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "nvidia,num-SS", + 0); + nr_as = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "nvidia,num-AS", + 0); + thsp->db_base = (1 + (nr_sm >> 1) + nr_ss + nr_as) << 16; + + return 0; +} + +static const struct udevice_id tegra_hsp_ids[] = { + { .compatible = "nvidia,tegra186-hsp" }, + { } +}; + +struct mbox_ops tegra_hsp_mbox_ops = { + .request = tegra_hsp_request, + .free = tegra_hsp_free, + .send = tegra_hsp_send, + .recv = tegra_hsp_recv, +}; + +U_BOOT_DRIVER(tegra_hsp) = { + .name = "tegra-hsp", + .id = UCLASS_MAILBOX, + .of_match = tegra_hsp_ids, + .bind = tegra_hsp_bind, + .probe = tegra_hsp_probe, + .priv_auto_alloc_size = sizeof(struct tegra_hsp), + .ops = &tegra_hsp_mbox_ops, +}; -- cgit v1.2.1