summaryrefslogtreecommitdiff
path: root/drivers/misc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/misc')
-rw-r--r--drivers/misc/Kconfig30
-rw-r--r--drivers/misc/Makefile11
-rw-r--r--drivers/misc/cros_ec.c9
-rw-r--r--drivers/misc/cros_ec_sandbox.c11
-rw-r--r--drivers/misc/i2c_eeprom.c39
-rw-r--r--drivers/misc/misc-uclass.c11
-rw-r--r--drivers/misc/nuvoton_nct6102d.c56
-rw-r--r--drivers/misc/spltest_sandbox.c53
-rw-r--r--drivers/misc/sysreset-uclass.c81
-rw-r--r--drivers/misc/sysreset_sandbox.c101
-rw-r--r--drivers/misc/tegra186_bpmp.c257
11 files changed, 451 insertions, 208 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 2373037685..8990489835 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -90,6 +90,14 @@ config MXC_OCOTP
Programmable memory pages that are stored on the some
Freescale i.MX processors.
+config NUVOTON_NCT6102D
+ bool "Enable Nuvoton NCT6102D Super I/O driver"
+ help
+ If you say Y here, you will get support for the Nuvoton
+ NCT6102D Super I/O driver. This can be used to enable or
+ disable the legacy UART, the watchdog or other devices
+ in the Nuvoton Super IO chips on X86 platforms.
+
config PWRSEQ
bool "Enable power-sequencing drivers"
depends on DM
@@ -121,14 +129,17 @@ config PCA9551_I2C_ADDR
help
The I2C address of the PCA9551 LED controller.
-config SYSRESET
- bool "Enable support for system reset drivers"
- depends on DM
+config TEGRA186_BPMP
+ bool "Enable support for the Tegra186 BPMP driver"
+ depends on TEGRA186
help
- Enable system reset drivers which can be used to reset the CPU or
- board. Each driver can provide a reset method which will be called
- to effect a reset. The uclass will try all available drivers when
- reset_walk() is called.
+ 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.
config WINBOND_W83627
bool "Enable Winbond Super I/O driver"
@@ -144,4 +155,9 @@ config QFW
Hidden option to enable QEMU fw_cfg interface. This will be selected by
either CONFIG_CMD_QFW or CONFIG_GENERATE_ACPI_TABLE.
+config I2C_EEPROM
+ bool "Enable driver for generic I2C-attached EEPROMs"
+ depends on MISC
+ help
+ Enable a generic driver for EEPROMs attached via I2C.
endmenu
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 066639ba1f..c0e5f03f8c 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -24,23 +24,30 @@ obj-$(CONFIG_I2C_EEPROM) += i2c_eeprom.o
obj-$(CONFIG_FSL_MC9SDZ60) += mc9sdz60.o
obj-$(CONFIG_MXC_OCOTP) += mxc_ocotp.o
obj-$(CONFIG_MXS_OCOTP) += mxs_ocotp.o
+obj-$(CONFIG_NUVOTON_NCT6102D) += nuvoton_nct6102d.o
obj-$(CONFIG_NS87308) += ns87308.o
obj-$(CONFIG_PDSP188x) += pdsp188x.o
obj-$(CONFIG_$(SPL_)PWRSEQ) += pwrseq-uclass.o
-obj-$(CONFIG_SANDBOX) += sysreset_sandbox.o
ifdef CONFIG_DM_I2C
+ifndef CONFIG_SPL_BUILD
obj-$(CONFIG_SANDBOX) += i2c_eeprom_emul.o
endif
+endif
obj-$(CONFIG_SMSC_LPC47M) += smsc_lpc47m.o
obj-$(CONFIG_SMSC_SIO1007) += smsc_sio1007.o
obj-$(CONFIG_STATUS_LED) += status_led.o
obj-$(CONFIG_SANDBOX) += swap_case.o
+ifdef CONFIG_SPL_OF_PLATDATA
+ifdef CONFIG_SPL_BUILD
+obj-$(CONFIG_SANDBOX) += spltest_sandbox.o
+endif
+endif
obj-$(CONFIG_SANDBOX) += syscon_sandbox.o
+obj-$(CONFIG_TEGRA186_BPMP) += tegra186_bpmp.o
obj-$(CONFIG_TWL4030_LED) += twl4030_led.o
obj-$(CONFIG_FSL_IFC) += fsl_ifc.o
obj-$(CONFIG_FSL_SEC_MON) += fsl_sec_mon.o
obj-$(CONFIG_PCA9551_LED) += pca9551_led.o
-obj-$(CONFIG_SYSRESET) += sysreset-uclass.o
obj-$(CONFIG_FSL_DEVICE_DISABLE) += fsl_devdis.o
obj-$(CONFIG_WINBOND_W83627) += winbond_w83627.o
obj-$(CONFIG_QFW) += qfw.o
diff --git a/drivers/misc/cros_ec.c b/drivers/misc/cros_ec.c
index e3229efed0..aea8d61f34 100644
--- a/drivers/misc/cros_ec.c
+++ b/drivers/misc/cros_ec.c
@@ -26,7 +26,6 @@
#include <asm/io.h>
#include <asm-generic/gpio.h>
#include <dm/device-internal.h>
-#include <dm/root.h>
#include <dm/uclass-internal.h>
#ifdef DEBUG_TRACE
@@ -1450,12 +1449,6 @@ static int do_cros_ec(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
return ret;
}
-int cros_ec_post_bind(struct udevice *dev)
-{
- /* Scan for available EC devices (e.g. I2C tunnel) */
- return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false);
-}
-
U_BOOT_CMD(
crosec, 6, 1, do_cros_ec,
"CROS-EC utility command",
@@ -1482,5 +1475,5 @@ UCLASS_DRIVER(cros_ec) = {
.id = UCLASS_CROS_EC,
.name = "cros_ec",
.per_device_auto_alloc_size = sizeof(struct cros_ec_dev),
- .post_bind = cros_ec_post_bind,
+ .post_bind = dm_scan_fdt_dev,
};
diff --git a/drivers/misc/cros_ec_sandbox.c b/drivers/misc/cros_ec_sandbox.c
index 98f19a68bf..c4fbca0d3a 100644
--- a/drivers/misc/cros_ec_sandbox.c
+++ b/drivers/misc/cros_ec_sandbox.c
@@ -517,6 +517,7 @@ int cros_ec_probe(struct udevice *dev)
struct ec_state *ec = dev->priv;
struct cros_ec_dev *cdev = dev->uclass_priv;
const void *blob = gd->fdt_blob;
+ struct udevice *keyb_dev;
int node;
int err;
@@ -525,7 +526,15 @@ int cros_ec_probe(struct udevice *dev)
if (err)
return err;
- node = fdtdec_next_compatible(blob, 0, COMPAT_GOOGLE_CROS_EC_KEYB);
+ node = -1;
+ for (device_find_first_child(dev, &keyb_dev);
+ keyb_dev;
+ device_find_next_child(&keyb_dev)) {
+ if (device_get_uclass_id(keyb_dev) == UCLASS_KEYBOARD) {
+ node = keyb_dev->of_offset;
+ break;
+ }
+ }
if (node < 0) {
debug("%s: No cros_ec keyboard found\n", __func__);
} else if (keyscan_read_fdt_matrix(ec, blob, node)) {
diff --git a/drivers/misc/i2c_eeprom.c b/drivers/misc/i2c_eeprom.c
index 814134a2cb..c9f4174bad 100644
--- a/drivers/misc/i2c_eeprom.c
+++ b/drivers/misc/i2c_eeprom.c
@@ -13,7 +13,7 @@
static int i2c_eeprom_read(struct udevice *dev, int offset, uint8_t *buf,
int size)
{
- return -ENODEV;
+ return dm_i2c_read(dev, offset, buf, size);
}
static int i2c_eeprom_write(struct udevice *dev, int offset,
@@ -27,23 +27,46 @@ struct i2c_eeprom_ops i2c_eeprom_std_ops = {
.write = i2c_eeprom_write,
};
+static int i2c_eeprom_std_ofdata_to_platdata(struct udevice *dev)
+{
+ struct i2c_eeprom *priv = dev_get_priv(dev);
+ u64 data = dev_get_driver_data(dev);
+
+ /* 6 bit -> page size of up to 2^63 (should be sufficient) */
+ priv->pagewidth = data & 0x3F;
+ priv->pagesize = (1 << priv->pagewidth);
+
+ return 0;
+}
+
int i2c_eeprom_std_probe(struct udevice *dev)
{
return 0;
}
static const struct udevice_id i2c_eeprom_std_ids[] = {
- { .compatible = "i2c-eeprom" },
+ { .compatible = "i2c-eeprom", .data = 0 },
+ { .compatible = "atmel,24c01a", .data = 3 },
+ { .compatible = "atmel,24c02", .data = 3 },
+ { .compatible = "atmel,24c04", .data = 4 },
+ { .compatible = "atmel,24c08a", .data = 4 },
+ { .compatible = "atmel,24c16a", .data = 4 },
+ { .compatible = "atmel,24c32", .data = 5 },
+ { .compatible = "atmel,24c64", .data = 5 },
+ { .compatible = "atmel,24c128", .data = 6 },
+ { .compatible = "atmel,24c256", .data = 6 },
+ { .compatible = "atmel,24c512", .data = 6 },
{ }
};
U_BOOT_DRIVER(i2c_eeprom_std) = {
- .name = "i2c_eeprom",
- .id = UCLASS_I2C_EEPROM,
- .of_match = i2c_eeprom_std_ids,
- .probe = i2c_eeprom_std_probe,
- .priv_auto_alloc_size = sizeof(struct i2c_eeprom),
- .ops = &i2c_eeprom_std_ops,
+ .name = "i2c_eeprom",
+ .id = UCLASS_I2C_EEPROM,
+ .of_match = i2c_eeprom_std_ids,
+ .probe = i2c_eeprom_std_probe,
+ .ofdata_to_platdata = i2c_eeprom_std_ofdata_to_platdata,
+ .priv_auto_alloc_size = sizeof(struct i2c_eeprom),
+ .ops = &i2c_eeprom_std_ops,
};
UCLASS_DRIVER(i2c_eeprom) = {
diff --git a/drivers/misc/misc-uclass.c b/drivers/misc/misc-uclass.c
index 13a6ea508b..d9eea3dac5 100644
--- a/drivers/misc/misc-uclass.c
+++ b/drivers/misc/misc-uclass.c
@@ -45,6 +45,17 @@ int misc_ioctl(struct udevice *dev, unsigned long request, void *buf)
return ops->ioctl(dev, request, buf);
}
+int misc_call(struct udevice *dev, int msgid, void *tx_msg, int tx_size,
+ void *rx_msg, int rx_size)
+{
+ const struct misc_ops *ops = device_get_ops(dev);
+
+ if (!ops->call)
+ return -ENOSYS;
+
+ return ops->call(dev, msgid, tx_msg, tx_size, rx_msg, rx_size);
+}
+
UCLASS_DRIVER(misc) = {
.id = UCLASS_MISC,
.name = "misc",
diff --git a/drivers/misc/nuvoton_nct6102d.c b/drivers/misc/nuvoton_nct6102d.c
new file mode 100644
index 0000000000..ced70f178b
--- /dev/null
+++ b/drivers/misc/nuvoton_nct6102d.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2016 Stefan Roese <sr@denx.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <nuvoton_nct6102d.h>
+#include <asm/io.h>
+#include <asm/pnp_def.h>
+
+static void superio_outb(int reg, int val)
+{
+ outb(reg, NCT_EFER);
+ outb(val, NCT_EFDR);
+}
+
+static inline int superio_inb(int reg)
+{
+ outb(reg, NCT_EFER);
+ return inb(NCT_EFDR);
+}
+
+static int superio_enter(void)
+{
+ outb(NCT_ENTRY_KEY, NCT_EFER); /* Enter extended function mode */
+ outb(NCT_ENTRY_KEY, NCT_EFER); /* Again according to manual */
+
+ return 0;
+}
+
+static void superio_select(int ld)
+{
+ superio_outb(NCT_LD_SELECT_REG, ld);
+}
+
+static void superio_exit(void)
+{
+ outb(NCT_EXIT_KEY, NCT_EFER); /* Leave extended function mode */
+}
+
+/*
+ * The Nuvoton NCT6102D starts per default after reset with both,
+ * the internal watchdog and the internal legacy UART enabled. This
+ * code provides a function to disable the watchdog.
+ */
+int nct6102d_wdt_disable(void)
+{
+ superio_enter();
+ /* Select logical device for WDT */
+ superio_select(NCT6102D_LD_WDT);
+ superio_outb(NCT6102D_WDT_TIMEOUT, 0x00);
+ superio_exit();
+
+ return 0;
+}
diff --git a/drivers/misc/spltest_sandbox.c b/drivers/misc/spltest_sandbox.c
new file mode 100644
index 0000000000..1fef8252ab
--- /dev/null
+++ b/drivers/misc/spltest_sandbox.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2016 Google, Inc
+ * Written by Simon Glass <sjg@chromium.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <dt-structs.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static int sandbox_spl_probe(struct udevice *dev)
+{
+ struct dtd_sandbox_spl_test *plat = dev_get_platdata(dev);
+ int i;
+
+ printf("of-platdata probe:\n");
+ printf("bool %d\n", plat->boolval);
+
+ printf("byte %02x\n", plat->byteval);
+ printf("bytearray");
+ for (i = 0; i < sizeof(plat->bytearray); i++)
+ printf(" %02x", plat->bytearray[i]);
+ printf("\n");
+
+ printf("int %d\n", plat->intval);
+ printf("intarray");
+ for (i = 0; i < ARRAY_SIZE(plat->intarray); i++)
+ printf(" %d", plat->intarray[i]);
+ printf("\n");
+
+ printf("longbytearray");
+ for (i = 0; i < sizeof(plat->longbytearray); i++)
+ printf(" %02x", plat->longbytearray[i]);
+ printf("\n");
+
+ printf("string %s\n", plat->stringval);
+ printf("stringarray");
+ for (i = 0; i < ARRAY_SIZE(plat->stringarray); i++)
+ printf(" \"%s\"", plat->stringarray[i]);
+ printf("\n");
+
+ return 0;
+}
+
+U_BOOT_DRIVER(sandbox_spl_test) = {
+ .name = "sandbox_spl_test",
+ .id = UCLASS_MISC,
+ .flags = DM_FLAG_PRE_RELOC,
+ .probe = sandbox_spl_probe,
+};
diff --git a/drivers/misc/sysreset-uclass.c b/drivers/misc/sysreset-uclass.c
deleted file mode 100644
index 3566d17fb1..0000000000
--- a/drivers/misc/sysreset-uclass.c
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2015 Google, Inc
- * Written by Simon Glass <sjg@chromium.org>
- *
- * SPDX-License-Identifier: GPL-2.0+
- */
-
-#include <common.h>
-#include <sysreset.h>
-#include <dm.h>
-#include <errno.h>
-#include <regmap.h>
-#include <dm/device-internal.h>
-#include <dm/lists.h>
-#include <dm/root.h>
-#include <linux/err.h>
-
-int sysreset_request(struct udevice *dev, enum sysreset_t type)
-{
- struct sysreset_ops *ops = sysreset_get_ops(dev);
-
- if (!ops->request)
- return -ENOSYS;
-
- return ops->request(dev, type);
-}
-
-int sysreset_walk(enum sysreset_t type)
-{
- struct udevice *dev;
- int ret = -ENOSYS;
-
- while (ret != -EINPROGRESS && type < SYSRESET_COUNT) {
- for (uclass_first_device(UCLASS_SYSRESET, &dev);
- dev;
- uclass_next_device(&dev)) {
- ret = sysreset_request(dev, type);
- if (ret == -EINPROGRESS)
- break;
- }
- type++;
- }
-
- return ret;
-}
-
-void sysreset_walk_halt(enum sysreset_t type)
-{
- int ret;
-
- ret = sysreset_walk(type);
-
- /* Wait for the reset to take effect */
- if (ret == -EINPROGRESS)
- mdelay(100);
-
- /* Still no reset? Give up */
- debug("System reset not supported on this platform\n");
- hang();
-}
-
-/**
- * reset_cpu() - calls sysreset_walk(SYSRESET_WARM)
- */
-void reset_cpu(ulong addr)
-{
- sysreset_walk_halt(SYSRESET_WARM);
-}
-
-
-int do_reset(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
-{
- sysreset_walk_halt(SYSRESET_WARM);
-
- return 0;
-}
-
-UCLASS_DRIVER(sysreset) = {
- .id = UCLASS_SYSRESET,
- .name = "sysreset",
-};
diff --git a/drivers/misc/sysreset_sandbox.c b/drivers/misc/sysreset_sandbox.c
deleted file mode 100644
index 7ae7f386ee..0000000000
--- a/drivers/misc/sysreset_sandbox.c
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (c) 2015 Google, Inc
- * Written by Simon Glass <sjg@chromium.org>
- *
- * SPDX-License-Identifier: GPL-2.0+
- */
-
-#include <common.h>
-#include <dm.h>
-#include <errno.h>
-#include <sysreset.h>
-#include <asm/state.h>
-#include <asm/test.h>
-
-DECLARE_GLOBAL_DATA_PTR;
-
-static int sandbox_warm_sysreset_request(struct udevice *dev,
- enum sysreset_t type)
-{
- struct sandbox_state *state = state_get_current();
-
- switch (type) {
- case SYSRESET_WARM:
- state->last_sysreset = type;
- break;
- default:
- return -ENOSYS;
- }
- if (!state->sysreset_allowed[type])
- return -EACCES;
-
- return -EINPROGRESS;
-}
-
-static int sandbox_sysreset_request(struct udevice *dev, enum sysreset_t type)
-{
- struct sandbox_state *state = state_get_current();
-
- /*
- * If we have a device tree, the device we created from platform data
- * (see the U_BOOT_DEVICE() declaration below) should not do anything.
- * If we are that device, return an error.
- */
- if (state->fdt_fname && dev->of_offset == -1)
- return -ENODEV;
-
- switch (type) {
- case SYSRESET_COLD:
- state->last_sysreset = type;
- break;
- case SYSRESET_POWER:
- state->last_sysreset = type;
- if (!state->sysreset_allowed[type])
- return -EACCES;
- sandbox_exit();
- break;
- default:
- return -ENOSYS;
- }
- if (!state->sysreset_allowed[type])
- return -EACCES;
-
- return -EINPROGRESS;
-}
-
-static struct sysreset_ops sandbox_sysreset_ops = {
- .request = sandbox_sysreset_request,
-};
-
-static const struct udevice_id sandbox_sysreset_ids[] = {
- { .compatible = "sandbox,reset" },
- { }
-};
-
-U_BOOT_DRIVER(sysreset_sandbox) = {
- .name = "sysreset_sandbox",
- .id = UCLASS_SYSRESET,
- .of_match = sandbox_sysreset_ids,
- .ops = &sandbox_sysreset_ops,
-};
-
-static struct sysreset_ops sandbox_warm_sysreset_ops = {
- .request = sandbox_warm_sysreset_request,
-};
-
-static const struct udevice_id sandbox_warm_sysreset_ids[] = {
- { .compatible = "sandbox,warm-reset" },
- { }
-};
-
-U_BOOT_DRIVER(warm_sysreset_sandbox) = {
- .name = "warm_sysreset_sandbox",
- .id = UCLASS_SYSRESET,
- .of_match = sandbox_warm_sysreset_ids,
- .ops = &sandbox_warm_sysreset_ops,
-};
-
-/* This is here in case we don't have a device tree */
-U_BOOT_DEVICE(sysreset_sandbox_non_fdt) = {
- .name = "sysreset_sandbox",
-};
diff --git a/drivers/misc/tegra186_bpmp.c b/drivers/misc/tegra186_bpmp.c
new file mode 100644
index 0000000000..f4ddbea376
--- /dev/null
+++ b/drivers/misc/tegra186_bpmp.c
@@ -0,0 +1,257 @@
+/*
+ * 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 <misc.h>
+#include <asm/arch-tegra/bpmp_abi.h>
+#include <asm/arch-tegra/ivc.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;
+};
+
+static int tegra186_bpmp_call(struct udevice *dev, int mrq, void *tx_msg,
+ int tx_size, void *rx_msg, int rx_size)
+{
+ 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=%d, rx_msg=%p, rx_size=%d) (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 rx_size;
+}
+
+/**
+ * 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;
+
+ ret = dm_scan_fdt_dev(dev);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static ulong tegra186_bpmp_get_shmem(struct udevice *dev, int index)
+{
+ int ret;
+ struct fdtdec_phandle_args args;
+ 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;
+ }
+
+ reg = fdtdec_get_addr_size_auto_noparent(gd->fdt_blob, args.node,
+ "reg", 0, NULL, true);
+ if (reg == FDT_ADDR_T_NONE) {
+ error("fdtdec_get_addr_size_auto_noparent() 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);
+}
+
+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 struct misc_ops tegra186_bpmp_ops = {
+ .call = tegra186_bpmp_call,
+};
+
+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,
+ .ops = &tegra186_bpmp_ops,
+ .priv_auto_alloc_size = sizeof(struct tegra186_bpmp),
+};