[U-Boot] [PATCH V4 1/4] clock: add Tegra186 clock driver

From: Stephen Warren swarren@nvidia.com
In Tegra186, on-SoC clocks are manipulated using IPC requests to the BPMP (Boot and Power Management Processor). This change implements a driver that does that. A tegra/ sub-directory is created to follow the existing pattern. It is unconditionally selected by CONFIG_TEGRA186 since virtually any Tegra186 build of U-Boot will need the feature.
Signed-off-by: Stephen Warren swarren@nvidia.com Reviewed-by: Simon Glass sjg@chromium.org --- v4: Adapt to new misc_call() return code. v3: Use misc class call op. v2: This patch wasn't posted. --- arch/arm/mach-tegra/Kconfig | 2 + drivers/clk/Kconfig | 1 + drivers/clk/Makefile | 2 + drivers/clk/tegra/Kconfig | 6 +++ drivers/clk/tegra/Makefile | 5 ++ drivers/clk/tegra/tegra186-clk.c | 104 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 120 insertions(+) create mode 100644 drivers/clk/tegra/Kconfig create mode 100644 drivers/clk/tegra/Makefile create mode 100644 drivers/clk/tegra/tegra186-clk.c
diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig index ec2d8ac6a1a3..e8186d515856 100644 --- a/arch/arm/mach-tegra/Kconfig +++ b/arch/arm/mach-tegra/Kconfig @@ -64,9 +64,11 @@ config TEGRA210
config TEGRA186 bool "Tegra186 family" + select CLK select DM_MAILBOX select MISC select TEGRA186_BPMP + select TEGRA186_CLOCK select TEGRA186_GPIO select TEGRA_ARMV8_COMMON select TEGRA_HSP diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 6eee8eb369bf..7dd56738b06a 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -20,6 +20,7 @@ config SPL_CLK setting up clocks within SPL, and allows the same drivers to be used as U-Boot proper.
+source "drivers/clk/tegra/Kconfig" source "drivers/clk/uniphier/Kconfig" source "drivers/clk/exynos/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 3cbdd54f4f3e..463b1d647dc6 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -10,5 +10,7 @@ obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ obj-$(CONFIG_SANDBOX) += clk_sandbox.o obj-$(CONFIG_SANDBOX) += clk_sandbox_test.o obj-$(CONFIG_MACH_PIC32) += clk_pic32.o + +obj-y += tegra/ obj-$(CONFIG_CLK_UNIPHIER) += uniphier/ obj-$(CONFIG_CLK_EXYNOS) += exynos/ diff --git a/drivers/clk/tegra/Kconfig b/drivers/clk/tegra/Kconfig new file mode 100644 index 000000000000..659fe022c2af --- /dev/null +++ b/drivers/clk/tegra/Kconfig @@ -0,0 +1,6 @@ +config TEGRA186_CLOCK + bool "Enable Tegra186 BPMP-based clock driver" + depends on TEGRA186_BPMP + help + Enable support for manipulating Tegra's on-SoC clocks via IPC + requests to the BPMP (Boot and Power Management Processor). diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile new file mode 100644 index 000000000000..f32998ccc27d --- /dev/null +++ b/drivers/clk/tegra/Makefile @@ -0,0 +1,5 @@ +# Copyright (c) 2016, NVIDIA CORPORATION. +# +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_TEGRA186_CLOCK) += tegra186-clk.o diff --git a/drivers/clk/tegra/tegra186-clk.c b/drivers/clk/tegra/tegra186-clk.c new file mode 100644 index 000000000000..075cb464cf6f --- /dev/null +++ b/drivers/clk/tegra/tegra186-clk.c @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <clk-uclass.h> +#include <dm.h> +#include <misc.h> +#include <asm/arch-tegra/bpmp_abi.h> + +static ulong tegra186_clk_get_rate(struct clk *clk) +{ + struct mrq_clk_request req; + struct mrq_clk_response resp; + int ret; + + debug("%s(clk=%p) (dev=%p, id=%lu)\n", __func__, clk, clk->dev, + clk->id); + + req.cmd_and_id = (CMD_CLK_GET_RATE << 24) | clk->id; + + ret = misc_call(clk->dev->parent, MRQ_CLK, &req, sizeof(req), &resp, + sizeof(resp)); + if (ret < 0) + return ret; + + return resp.clk_get_rate.rate; +} + +static ulong tegra186_clk_set_rate(struct clk *clk, ulong rate) +{ + struct mrq_clk_request req; + struct mrq_clk_response resp; + int ret; + + debug("%s(clk=%p, rate=%lu) (dev=%p, id=%lu)\n", __func__, clk, rate, + clk->dev, clk->id); + + req.cmd_and_id = (CMD_CLK_SET_RATE << 24) | clk->id; + req.clk_set_rate.rate = rate; + + ret = misc_call(clk->dev->parent, MRQ_CLK, &req, sizeof(req), &resp, + sizeof(resp)); + if (ret < 0) + return ret; + + return resp.clk_set_rate.rate; +} + +static int tegra186_clk_en_dis(struct clk *clk, + enum mrq_reset_commands cmd) +{ + struct mrq_clk_request req; + struct mrq_clk_response resp; + int ret; + + req.cmd_and_id = (cmd << 24) | clk->id; + + ret = misc_call(clk->dev->parent, MRQ_CLK, &req, sizeof(req), &resp, + sizeof(resp)); + if (ret < 0) + return ret; + + return 0; +} + +static int tegra186_clk_enable(struct clk *clk) +{ + debug("%s(clk=%p) (dev=%p, id=%lu)\n", __func__, clk, clk->dev, + clk->id); + + return tegra186_clk_en_dis(clk, CMD_CLK_ENABLE); +} + +static int tegra186_clk_disable(struct clk *clk) +{ + debug("%s(clk=%p) (dev=%p, id=%lu)\n", __func__, clk, clk->dev, + clk->id); + + return tegra186_clk_en_dis(clk, CMD_CLK_DISABLE); +} + +static struct clk_ops tegra186_clk_ops = { + .get_rate = tegra186_clk_get_rate, + .set_rate = tegra186_clk_set_rate, + .enable = tegra186_clk_enable, + .disable = tegra186_clk_disable, +}; + +static int tegra186_clk_probe(struct udevice *dev) +{ + debug("%s(dev=%p)\n", __func__, dev); + + return 0; +} + +U_BOOT_DRIVER(tegra186_clk) = { + .name = "tegra186_clk", + .id = UCLASS_CLK, + .probe = tegra186_clk_probe, + .ops = &tegra186_clk_ops, +};

From: Stephen Warren swarren@nvidia.com
In Tegra186, on-SoC reset signals are manipulated using IPC requests to the BPMP (Boot and Power Management Processor). This change implements a driver that does that. It is unconditionally selected by CONFIG_TEGRA186 since virtually any Tegra186 build of U-Boot will need the feature.
Signed-off-by: Stephen Warren swarren@nvidia.com Reviewed-by: Simon Glass sjg@chromium.org --- v4: Adapt to new misc_call() return code. v3: Use misc class call op. v2: This patch wasn't posted. --- arch/arm/mach-tegra/Kconfig | 2 ++ drivers/reset/Kconfig | 7 ++++ drivers/reset/Makefile | 1 + drivers/reset/tegra186-reset.c | 81 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 91 insertions(+) create mode 100644 drivers/reset/tegra186-reset.c
diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig index e8186d515856..1eaf40627254 100644 --- a/arch/arm/mach-tegra/Kconfig +++ b/arch/arm/mach-tegra/Kconfig @@ -66,10 +66,12 @@ config TEGRA186 bool "Tegra186 family" select CLK select DM_MAILBOX + select DM_RESET select MISC select TEGRA186_BPMP select TEGRA186_CLOCK select TEGRA186_GPIO + select TEGRA186_RESET select TEGRA_ARMV8_COMMON select TEGRA_HSP select TEGRA_IVC diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 0fe8cc3827f1..5b84f2178b71 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -20,4 +20,11 @@ config SANDBOX_RESET simply accepts requests to reset various HW modules without actually doing anything beyond a little error checking.
+config TEGRA186_RESET + bool "Enable Tegra186 BPMP-based reset driver" + depends on TEGRA186_BPMP + help + Enable support for manipulating Tegra's on-SoC reset signals via IPC + requests to the BPMP (Boot and Power Management Processor). + endmenu diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 71f3b219613e..ff0e0907758b 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_DM_RESET) += reset-uclass.o obj-$(CONFIG_SANDBOX_MBOX) += sandbox-reset.o obj-$(CONFIG_SANDBOX_MBOX) += sandbox-reset-test.o +obj-$(CONFIG_TEGRA186_RESET) += tegra186-reset.o diff --git a/drivers/reset/tegra186-reset.c b/drivers/reset/tegra186-reset.c new file mode 100644 index 000000000000..228adda0aa8b --- /dev/null +++ b/drivers/reset/tegra186-reset.c @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <dm.h> +#include <misc.h> +#include <reset-uclass.h> +#include <asm/arch-tegra/bpmp_abi.h> + +static int tegra186_reset_request(struct reset_ctl *reset_ctl) +{ + debug("%s(reset_ctl=%p) (dev=%p, id=%lu)\n", __func__, reset_ctl, + reset_ctl->dev, reset_ctl->id); + + return 0; +} + +static int tegra186_reset_free(struct reset_ctl *reset_ctl) +{ + debug("%s(reset_ctl=%p) (dev=%p, id=%lu)\n", __func__, reset_ctl, + reset_ctl->dev, reset_ctl->id); + + return 0; +} + +static int tegra186_reset_common(struct reset_ctl *reset_ctl, + enum mrq_reset_commands cmd) +{ + struct mrq_reset_request req; + int ret; + + req.cmd = cmd; + req.reset_id = reset_ctl->id; + + ret = misc_call(reset_ctl->dev->parent, MRQ_RESET, &req, sizeof(req), + NULL, 0); + if (ret < 0) + return ret; + + return 0; +} + +static int tegra186_reset_assert(struct reset_ctl *reset_ctl) +{ + debug("%s(reset_ctl=%p) (dev=%p, id=%lu)\n", __func__, reset_ctl, + reset_ctl->dev, reset_ctl->id); + + return tegra186_reset_common(reset_ctl, CMD_RESET_ASSERT); +} + +static int tegra186_reset_deassert(struct reset_ctl *reset_ctl) +{ + debug("%s(reset_ctl=%p) (dev=%p, id=%lu)\n", __func__, reset_ctl, + reset_ctl->dev, reset_ctl->id); + + return tegra186_reset_common(reset_ctl, CMD_RESET_DEASSERT); +} + +struct reset_ops tegra186_reset_ops = { + .request = tegra186_reset_request, + .free = tegra186_reset_free, + .rst_assert = tegra186_reset_assert, + .rst_deassert = tegra186_reset_deassert, +}; + +static int tegra186_reset_probe(struct udevice *dev) +{ + debug("%s(dev=%p)\n", __func__, dev); + + return 0; +} + +U_BOOT_DRIVER(tegra186_reset) = { + .name = "tegra186_reset", + .id = UCLASS_RESET, + .probe = tegra186_reset_probe, + .ops = &tegra186_reset_ops, +};

From: Stephen Warren swarren@nvidia.com
In Tegra186, SoC power domains are manipulated using IPC requests to the BPMP (Boot and Power Management Processor). This change implements a driver that does that.
Signed-off-by: Stephen Warren swarren@nvidia.com Reviewed-by: Simon Glass sjg@chromium.org --- v4: Adapt to new misc_call() return code. v3: Use misc class call op. v2: This patch wasn't posted. --- drivers/power/domain/Kconfig | 7 +++ drivers/power/domain/Makefile | 1 + drivers/power/domain/tegra186-power-domain.c | 92 ++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+) create mode 100644 drivers/power/domain/tegra186-power-domain.c
diff --git a/drivers/power/domain/Kconfig b/drivers/power/domain/Kconfig index b90409743398..132e33250e8c 100644 --- a/drivers/power/domain/Kconfig +++ b/drivers/power/domain/Kconfig @@ -17,4 +17,11 @@ config SANDBOX_POWER_DOMAIN simply accepts requests to power on/off various HW modules without actually doing anything beyond a little error checking.
+config TEGRA186_POWER_DOMAIN + bool "Enable Tegra186 BPMP-based power domain driver" + depends on TEGRA186_BPMP + help + Enable support for manipulating Tegra's on-SoC power domains via IPC + requests to the BPMP (Boot and Power Management Processor). + endmenu diff --git a/drivers/power/domain/Makefile b/drivers/power/domain/Makefile index c18292f0ec88..2c3d92638fbe 100644 --- a/drivers/power/domain/Makefile +++ b/drivers/power/domain/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_POWER_DOMAIN) += power-domain-uclass.o obj-$(CONFIG_SANDBOX_POWER_DOMAIN) += sandbox-power-domain.o obj-$(CONFIG_SANDBOX_POWER_DOMAIN) += sandbox-power-domain-test.o +obj-$(CONFIG_TEGRA186_POWER_DOMAIN) += tegra186-power-domain.o diff --git a/drivers/power/domain/tegra186-power-domain.c b/drivers/power/domain/tegra186-power-domain.c new file mode 100644 index 000000000000..41d84de83e1a --- /dev/null +++ b/drivers/power/domain/tegra186-power-domain.c @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <dm.h> +#include <misc.h> +#include <power-domain-uclass.h> +#include <asm/arch-tegra/bpmp_abi.h> + +#define UPDATE BIT(0) +#define ON BIT(1) + +static int tegra186_power_domain_request(struct power_domain *power_domain) +{ + debug("%s(power_domain=%p) (dev=%p, id=%lu)\n", __func__, + power_domain, power_domain->dev, power_domain->id); + + return 0; +} + +static int tegra186_power_domain_free(struct power_domain *power_domain) +{ + debug("%s(power_domain=%p) (dev=%p, id=%lu)\n", __func__, + power_domain, power_domain->dev, power_domain->id); + + return 0; +} + +static int tegra186_power_domain_common(struct power_domain *power_domain, + bool on) +{ + struct mrq_pg_update_state_request req; + int on_state = on ? ON : 0; + int ret; + + req.partition_id = power_domain->id; + req.logic_state = UPDATE | on_state; + req.sram_state = UPDATE | on_state; + /* + * Drivers manage their own clocks so they don't get out of sync, and + * since some power domains have many clocks, only a subset of which + * are actually needed depending on use-case. + */ + req.clock_state = UPDATE; + + ret = misc_call(power_domain->dev->parent, MRQ_PG_UPDATE_STATE, &req, + sizeof(req), NULL, 0); + if (ret < 0) + return ret; + + return 0; +} + +static int tegra186_power_domain_on(struct power_domain *power_domain) +{ + debug("%s(power_domain=%p) (dev=%p, id=%lu)\n", __func__, + power_domain, power_domain->dev, power_domain->id); + + return tegra186_power_domain_common(power_domain, true); +} + +static int tegra186_power_domain_off(struct power_domain *power_domain) +{ + debug("%s(power_domain=%p) (dev=%p, id=%lu)\n", __func__, + power_domain, power_domain->dev, power_domain->id); + + return tegra186_power_domain_common(power_domain, false); +} + +struct power_domain_ops tegra186_power_domain_ops = { + .request = tegra186_power_domain_request, + .free = tegra186_power_domain_free, + .on = tegra186_power_domain_on, + .off = tegra186_power_domain_off, +}; + +static int tegra186_power_domain_probe(struct udevice *dev) +{ + debug("%s(dev=%p)\n", __func__, dev); + + return 0; +} + +U_BOOT_DRIVER(tegra186_power_domain) = { + .name = "tegra186_power_domain", + .id = UCLASS_POWER_DOMAIN, + .probe = tegra186_power_domain_probe, + .ops = &tegra186_power_domain_ops, +};

From: Stephen Warren swarren@nvidia.com
On Tegra186, some I2C controllers are directly controlled by the main CPU, whereas others are controlled by the BPMP, and can only be accessed by the main CPU via IPC requests to the BPMP. This driver covers the latter case.
Signed-off-by: Stephen Warren swarren@nvidia.com Reviewed-by: Simon Glass sjg@chromium.org Reviewed-by: Heiko Schocher hs@denx.de --- v4: Adapt to new misc_call() return code. v3: Use misc class call op. v2: This patch wasn't posted. --- drivers/i2c/Kconfig | 10 ++++ drivers/i2c/Makefile | 1 + drivers/i2c/tegra186_bpmp_i2c.c | 129 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 140 insertions(+) create mode 100644 drivers/i2c/tegra186_bpmp_i2c.c
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index b3e84052ebbf..67ebec7c5733 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -161,6 +161,16 @@ config SYS_I2C_MVTWSI Support for Marvell I2C controllers as used on the orion5x and kirkwood SoC families.
+config TEGRA186_BPMP_I2C + bool "Enable Tegra186 BPMP-based I2C driver" + depends on TEGRA186_BPMP + help + Support for Tegra I2C controllers managed by the BPMP (Boot and + Power Management Processor). On Tegra186, some I2C controllers are + directly controlled by the main CPU, whereas others are controlled + by the BPMP, and can only be accessed by the main CPU via IPC + requests to the BPMP. This driver covers the latter case. + source "drivers/i2c/muxes/Kconfig"
endmenu diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 167424db9820..97b6bedde822 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -41,5 +41,6 @@ obj-$(CONFIG_SYS_I2C_TEGRA) += tegra_i2c.o obj-$(CONFIG_SYS_I2C_UNIPHIER) += i2c-uniphier.o obj-$(CONFIG_SYS_I2C_UNIPHIER_F) += i2c-uniphier-f.o obj-$(CONFIG_SYS_I2C_ZYNQ) += zynq_i2c.o +obj-$(CONFIG_TEGRA186_BPMP_I2C) += tegra186_bpmp_i2c.o
obj-$(CONFIG_I2C_MUX) += muxes/ diff --git a/drivers/i2c/tegra186_bpmp_i2c.c b/drivers/i2c/tegra186_bpmp_i2c.c new file mode 100644 index 000000000000..88e8413d9e07 --- /dev/null +++ b/drivers/i2c/tegra186_bpmp_i2c.c @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <dm.h> +#include <i2c.h> +#include <misc.h> +#include <asm/arch-tegra/bpmp_abi.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct tegra186_bpmp_i2c { + uint32_t bpmp_bus_id; +}; + +static inline void serialize_u16(uint8_t **p, uint16_t val) +{ + (*p)[0] = val & 0xff; + (*p)[1] = val >> 8; + (*p) += 2; +} + +/* These just happen to have the same values as I2C_M_* and SERIALI2C_* */ +#define SUPPORTED_FLAGS \ + (I2C_M_TEN | \ + I2C_M_RD | \ + I2C_M_STOP | \ + I2C_M_NOSTART | \ + I2C_M_REV_DIR_ADDR | \ + I2C_M_IGNORE_NAK | \ + I2C_M_NO_RD_ACK | \ + I2C_M_RECV_LEN) + +static int tegra186_bpmp_i2c_xfer(struct udevice *dev, struct i2c_msg *msg, + int nmsgs) +{ + struct tegra186_bpmp_i2c *priv = dev_get_priv(dev); + struct mrq_i2c_request req; + struct mrq_i2c_response resp; + uint8_t *p; + int left, i, ret; + + req.cmd = CMD_I2C_XFER; + req.xfer.bus_id = priv->bpmp_bus_id; + p = &req.xfer.data_buf[0]; + left = ARRAY_SIZE(req.xfer.data_buf); + for (i = 0; i < nmsgs; i++) { + int len = 6; + if (!(msg[i].flags & I2C_M_RD)) + len += msg[i].len; + if ((len >= BIT(16)) || (len > left)) + return -ENOSPC; + + if (msg[i].flags & ~SUPPORTED_FLAGS) + return -EINVAL; + + serialize_u16(&p, msg[i].addr); + serialize_u16(&p, msg[i].flags); + serialize_u16(&p, msg[i].len); + if (!(msg[i].flags & I2C_M_RD)) { + memcpy(p, msg[i].buf, msg[i].len); + p += msg[i].len; + } + } + req.xfer.data_size = p - &req.xfer.data_buf[0]; + + ret = misc_call(dev->parent, MRQ_I2C, &req, sizeof(req), &resp, + sizeof(resp)); + if (ret < 0) + return ret; + + p = &resp.xfer.data_buf[0]; + left = resp.xfer.data_size; + if (left > ARRAY_SIZE(resp.xfer.data_buf)) + return -EINVAL; + for (i = 0; i < nmsgs; i++) { + if (msg[i].flags & I2C_M_RD) { + memcpy(msg[i].buf, p, msg[i].len); + p += msg[i].len; + } + } + + return 0; +} + +static int tegra186_bpmp_i2c_probe(struct udevice *dev) +{ + struct tegra186_bpmp_i2c *priv = dev_get_priv(dev); + int ret; + struct fdtdec_phandle_args args; + + ret = fdtdec_parse_phandle_with_args(gd->fdt_blob, dev->of_offset, + "nvidia,bpmp", NULL, 0, 0, &args); + if (ret < 0) { + debug("%s: fdtdec_parse_phandle_with_args() failed: %d\n", + __func__, ret); + return ret; + } + + priv->bpmp_bus_id = fdtdec_get_uint(gd->fdt_blob, dev->of_offset, + "nvidia,bpmp-bus-id", U32_MAX); + if (priv->bpmp_bus_id == U32_MAX) { + debug("%s: could not parse nvidia,bpmp-bus-id\n", __func__); + return -ENODEV; + } + + return 0; +} + +static const struct dm_i2c_ops tegra186_bpmp_i2c_ops = { + .xfer = tegra186_bpmp_i2c_xfer, +}; + +static const struct udevice_id tegra186_bpmp_i2c_ids[] = { + { .compatible = "nvidia,tegra186-bpmp-i2c" }, + { } +}; + +U_BOOT_DRIVER(i2c_gpio) = { + .name = "tegra186_bpmp_i2c", + .id = UCLASS_I2C, + .of_match = tegra186_bpmp_i2c_ids, + .probe = tegra186_bpmp_i2c_probe, + .priv_auto_alloc_size = sizeof(struct tegra186_bpmp_i2c), + .ops = &tegra186_bpmp_i2c_ops, +};
participants (1)
-
Stephen Warren