Hi Stephen, On 27 July 2016 at 15:24, Stephen Warren <swar...@wwwdotorg.org> wrote: > From: Stephen Warren <swar...@nvidia.com> > > The Tegra BPMP (Boot and Power Management Processor) is a separate > auxiliary CPU embedded into Tegra to perform power management work, and > controls related features such as clocks, resets, power domains, PMIC I2C > bus, etc. This driver provides the core low-level communication path by > which feature-specific drivers (such as clock) can make requests to the > BPMP. This driver is similar to an MFD driver in the Linux kernel. It is > unconditionally selected by CONFIG_TEGRA186 since virtually any Tegra186 > build of U-Boot will need the feature. > > Signed-off-by: Stephen Warren <swar...@nvidia.com> > --- > arch/arm/include/asm/arch-tegra/bpmp_abi.h | 1601 > +++++++++++++++++++++++ > arch/arm/include/asm/arch-tegra/tegra186_bpmp.h | 14 + > arch/arm/mach-tegra/Kconfig | 2 + > drivers/misc/Kconfig | 12 + > drivers/misc/Makefile | 1 + > drivers/misc/tegra186_bpmp.c | 250 ++++ > 6 files changed, 1880 insertions(+) > create mode 100644 arch/arm/include/asm/arch-tegra/bpmp_abi.h > create mode 100644 arch/arm/include/asm/arch-tegra/tegra186_bpmp.h > create mode 100644 drivers/misc/tegra186_bpmp.c > > diff --git a/arch/arm/include/asm/arch-tegra/bpmp_abi.h > b/arch/arm/include/asm/arch-tegra/bpmp_abi.h > new file mode 100644 > index 000000000000..0aaef5960e29 > --- /dev/null > +++ b/arch/arm/include/asm/arch-tegra/bpmp_abi.h > @@ -0,0 +1,1601 @@ > +/* > + * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved. > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms and conditions of the GNU General Public License, > + * version 2, as published by the Free Software Foundation. > + * > + * This program is distributed in the hope it will be useful, but WITHOUT > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or > + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for > + * more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program. If not, see <http://www.gnu.org/licenses/>.
Can this use SPDX? Or if it is generated, where from? > + */ > + [...] > diff --git a/drivers/misc/tegra186_bpmp.c b/drivers/misc/tegra186_bpmp.c > new file mode 100644 > index 000000000000..147528da9269 > --- /dev/null > +++ b/drivers/misc/tegra186_bpmp.c > @@ -0,0 +1,250 @@ > +/* > + * Copyright (c) 2016, NVIDIA CORPORATION. > + * > + * SPDX-License-Identifier: GPL-2.0 > + */ > + > +#include <common.h> > +#include <dm.h> > +#include <dm/lists.h> > +#include <dm/root.h> > +#include <mailbox.h> > +#include <asm/arch-tegra/bpmp_abi.h> > +#include <asm/arch-tegra/ivc.h> > +#include <asm/arch-tegra/tegra186_bpmp.h> > + > +#define BPMP_IVC_FRAME_COUNT 1 > +#define BPMP_IVC_FRAME_SIZE 128 > + > +#define BPMP_FLAG_DO_ACK BIT(0) > +#define BPMP_FLAG_RING_DOORBELL BIT(1) > + > +DECLARE_GLOBAL_DATA_PTR; > + > +struct tegra186_bpmp { > + struct mbox_chan mbox; > + struct tegra_ivc ivc; > +}; > + > +int tegra186_bpmp_call(struct udevice *dev, uint32_t mrq, > + void *tx_msg, uint32_t tx_size, > + void *rx_msg, uint32_t rx_size) Function comment? Also why is this exported? Shouldn't calls come in via DM?? > +{ > + struct tegra186_bpmp *priv = dev_get_priv(dev); > + int ret, err; > + void *ivc_frame; > + struct mrq_request *req; > + struct mrq_response *resp; > + ulong start_time; > + > + debug("%s(dev=%p, mrq=%u, tx_msg=%p, tx_size=%u, rx_msg=%p, > rx_size=%u) (priv=%p)\n", > + __func__, dev, mrq, tx_msg, tx_size, rx_msg, rx_size, priv); > + > + if ((tx_size > BPMP_IVC_FRAME_SIZE) || (rx_size > > BPMP_IVC_FRAME_SIZE)) > + return -EINVAL; > + > + ret = tegra_ivc_write_get_next_frame(&priv->ivc, &ivc_frame); > + if (ret) { > + error("tegra_ivc_write_get_next_frame() failed: %d\n", ret); > + return ret; > + } > + > + req = ivc_frame; > + req->mrq = mrq; > + req->flags = BPMP_FLAG_DO_ACK | BPMP_FLAG_RING_DOORBELL; > + memcpy(req + 1, tx_msg, tx_size); > + > + ret = tegra_ivc_write_advance(&priv->ivc); > + if (ret) { > + error("tegra_ivc_write_advance() failed: %d\n", ret); > + return ret; > + } > + > + start_time = timer_get_us(); > + for (;;) { > + ret = tegra_ivc_channel_notified(&priv->ivc); > + if (ret) { > + error("tegra_ivc_channel_notified() failed: %d\n", > ret); > + return ret; > + } > + > + ret = tegra_ivc_read_get_next_frame(&priv->ivc, &ivc_frame); > + if (!ret) > + break; > + > + /* Timeout 20ms; roughly 10x current max observed duration */ > + if ((timer_get_us() - start_time) > 20 * 1000) { > + error("tegra_ivc_read_get_next_frame() timed out > (%d)\n", > + ret); > + return -ETIMEDOUT; > + } > + } > + > + resp = ivc_frame; > + err = resp->err; > + if (!err && rx_msg && rx_size) > + memcpy(rx_msg, resp + 1, rx_size); > + > + ret = tegra_ivc_read_advance(&priv->ivc); > + if (ret) { > + error("tegra_ivc_write_advance() failed: %d\n", ret); > + return ret; > + } > + > + if (err) { > + error("BPMP responded with error %d\n", err); > + /* err isn't a U-Boot error code, so don't that */ > + return -EIO; > + } > + > + return 0; > +} > + > +/** > + * The BPMP exposes multiple different services. We create a sub-device for > + * each separate type of service, since each device must be of the > appropriate > + * UCLASS. > + */ > +static int tegra186_bpmp_bind(struct udevice *dev) > +{ > + int ret; > + struct udevice *child; > + > + debug("%s(dev=%p)\n", __func__, dev); > + > + ret = device_bind_driver_to_node(dev, "tegra186_clk", "tegra186_clk", > + dev->of_offset, &child); > + if (ret) > + return ret; > + > + ret = device_bind_driver_to_node(dev, "tegra186_reset", > + "tegra186_reset", dev->of_offset, > + &child); > + if (ret) > + return ret; > + > + ret = device_bind_driver_to_node(dev, "tegra186_power_domain", > + "tegra186_power_domain", > + dev->of_offset, &child); > + if (ret) > + return ret; What's happening here? Is there one device tree node with 3 devices? > + > + return 0; > +} > + > +static ulong tegra186_bpmp_get_shmem(struct udevice *dev, int index) > +{ > + int ret; > + struct fdtdec_phandle_args args; > + struct udevice fakedev; > + fdt_addr_t reg; > + > + ret = fdtdec_parse_phandle_with_args(gd->fdt_blob, dev->of_offset, > + "shmem", NULL, 0, index, &args); > + if (ret < 0) { > + error("fdtdec_parse_phandle_with_args() failed: %d\n", ret); > + return ret; > + } > + > + fakedev.of_offset = args.node; > + reg = dev_get_addr_index(&fakedev, 0); This is nasty. If you don't set fakedev.parent, how does this work? Can you instead call fdtdec_get_addr_size_auto_parent() or similar? > + if (reg == FDT_ADDR_T_NONE) { > + error("dev_get_addr_index() failed\n"); > + return -ENODEV; > + } > + > + return reg; > +} > + > +static void tegra186_bpmp_ivc_notify(struct tegra_ivc *ivc) > +{ > + struct tegra186_bpmp *priv = > + container_of(ivc, struct tegra186_bpmp, ivc); > + int ret; > + > + ret = mbox_send(&priv->mbox, NULL); > + if (ret) > + error("mbox_send() failed: %d\n", ret); Then why not return the error? > +} > + > +static int tegra186_bpmp_probe(struct udevice *dev) > +{ > + struct tegra186_bpmp *priv = dev_get_priv(dev); > + int ret; > + ulong tx_base, rx_base, start_time; > + > + debug("%s(dev=%p) (priv=%p)\n", __func__, dev, priv); > + > + ret = mbox_get_by_index(dev, 0, &priv->mbox); > + if (ret) { > + error("mbox_get_by_index() failed: %d\n", ret); > + return ret; > + } > + > + tx_base = tegra186_bpmp_get_shmem(dev, 0); > + if (IS_ERR_VALUE(tx_base)) { > + error("tegra186_bpmp_get_shmem failed for tx_base\n"); > + return tx_base; > + } > + rx_base = tegra186_bpmp_get_shmem(dev, 1); > + if (IS_ERR_VALUE(rx_base)) { > + error("tegra186_bpmp_get_shmem failed for rx_base\n"); > + return rx_base; > + } > + debug("shmem: rx=%lx, tx=%lx\n", rx_base, tx_base); > + > + ret = tegra_ivc_init(&priv->ivc, rx_base, tx_base, > BPMP_IVC_FRAME_COUNT, > + BPMP_IVC_FRAME_SIZE, tegra186_bpmp_ivc_notify); > + if (ret) { > + error("tegra_ivc_init() failed: %d\n", ret); > + return ret; > + } > + > + tegra_ivc_channel_reset(&priv->ivc); > + start_time = timer_get_us(); > + for (;;) { > + ret = tegra_ivc_channel_notified(&priv->ivc); > + if (!ret) > + break; > + > + /* Timeout 100ms */ > + if ((timer_get_us() - start_time) > 100 * 1000) { > + error("Initial IVC reset timed out (%d)\n", ret); > + ret = -ETIMEDOUT; > + goto err_free_mbox; > + } > + } > + > + return 0; > + > +err_free_mbox: > + mbox_free(&priv->mbox); > + > + return ret; > +} > + > +static int tegra186_bpmp_remove(struct udevice *dev) > +{ > + struct tegra186_bpmp *priv = dev_get_priv(dev); > + > + debug("%s(dev=%p) (priv=%p)\n", __func__, dev, priv); > + > + mbox_free(&priv->mbox); > + > + return 0; > +} > + > +static const struct udevice_id tegra186_bpmp_ids[] = { > + { .compatible = "nvidia,tegra186-bpmp" }, > + { } > +}; > + > +U_BOOT_DRIVER(tegra186_bpmp) = { > + .name = "tegra186_bpmp", > + .id = UCLASS_MISC, > + .of_match = tegra186_bpmp_ids, > + .bind = tegra186_bpmp_bind, > + .probe = tegra186_bpmp_probe, > + .remove = tegra186_bpmp_remove, > + .priv_auto_alloc_size = sizeof(struct tegra186_bpmp), > +}; > -- > 2.9.2 > Regards, Simon _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot