diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index da49a8383dc3..d3f1d96f75e9 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -1584,9 +1584,6 @@ EXPORT_SYMBOL_GPL(pm_genpd_remove); #ifdef CONFIG_PM_GENERIC_DOMAINS_OF -typedef struct generic_pm_domain *(*genpd_xlate_t)(struct of_phandle_args *args, - void *data); - /* * Device Tree based PM domain providers. * @@ -1742,6 +1739,9 @@ int of_genpd_add_provider_onecell(struct device_node *np, mutex_lock(&gpd_list_lock); + if (!data->xlate) + data->xlate = genpd_xlate_onecell; + for (i = 0; i < data->num_domains; i++) { if (!data->domains[i]) continue; @@ -1752,7 +1752,7 @@ int of_genpd_add_provider_onecell(struct device_node *np, data->domains[i]->has_provider = true; } - ret = genpd_add_provider(np, genpd_xlate_onecell, data); + ret = genpd_add_provider(np, data->xlate, data); if (ret < 0) goto error; diff --git a/drivers/firmware/tegra/bpmp.c b/drivers/firmware/tegra/bpmp.c index 84e4c9a58a0c..f11c7025b4a1 100644 --- a/drivers/firmware/tegra/bpmp.c +++ b/drivers/firmware/tegra/bpmp.c @@ -810,6 +810,10 @@ static int tegra_bpmp_probe(struct platform_device *pdev) if (err < 0) goto free_mrq; + err = tegra_bpmp_init_powergates(bpmp); + if (err < 0) + goto free_mrq; + platform_set_drvdata(pdev, bpmp); return 0; diff --git a/drivers/soc/tegra/Kconfig b/drivers/soc/tegra/Kconfig index dcf088db40b6..1beb7c347344 100644 --- a/drivers/soc/tegra/Kconfig +++ b/drivers/soc/tegra/Kconfig @@ -115,3 +115,8 @@ config SOC_TEGRA_PMC config SOC_TEGRA_PMC_TEGRA186 bool + +config SOC_TEGRA_POWERGATE_BPMP + def_bool y + depends on PM_GENERIC_DOMAINS + depends on TEGRA_BPMP diff --git a/drivers/soc/tegra/Makefile b/drivers/soc/tegra/Makefile index 4f81dd55e5d1..0e52b45721ac 100644 --- a/drivers/soc/tegra/Makefile +++ b/drivers/soc/tegra/Makefile @@ -4,3 +4,4 @@ obj-y += common.o obj-$(CONFIG_SOC_TEGRA_FLOWCTRL) += flowctrl.o obj-$(CONFIG_SOC_TEGRA_PMC) += pmc.o obj-$(CONFIG_SOC_TEGRA_PMC_TEGRA186) += pmc-tegra186.o +obj-$(CONFIG_SOC_TEGRA_POWERGATE_BPMP) += powergate-bpmp.o diff --git a/drivers/soc/tegra/flowctrl.c b/drivers/soc/tegra/flowctrl.c index 0e345c05fc65..5433cc7a043e 100644 --- a/drivers/soc/tegra/flowctrl.c +++ b/drivers/soc/tegra/flowctrl.c @@ -157,7 +157,7 @@ static int tegra_flowctrl_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); tegra_flowctrl_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(tegra_flowctrl_base)) - return PTR_ERR(base); + return PTR_ERR(tegra_flowctrl_base); iounmap(base); diff --git a/drivers/soc/tegra/powergate-bpmp.c b/drivers/soc/tegra/powergate-bpmp.c new file mode 100644 index 000000000000..8fc356039401 --- /dev/null +++ b/drivers/soc/tegra/powergate-bpmp.c @@ -0,0 +1,359 @@ +/* + * Copyright (c) 2016-2017, 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. + */ + +#include +#include +#include +#include +#include + +#include +#include + +struct tegra_powergate_info { + unsigned int id; + char *name; +}; + +struct tegra_powergate { + struct generic_pm_domain genpd; + struct tegra_bpmp *bpmp; + unsigned int id; +}; + +static inline struct tegra_powergate * +to_tegra_powergate(struct generic_pm_domain *genpd) +{ + return container_of(genpd, struct tegra_powergate, genpd); +} + +static int tegra_bpmp_powergate_set_state(struct tegra_bpmp *bpmp, + unsigned int id, u32 state) +{ + struct mrq_pg_request request; + struct tegra_bpmp_message msg; + + memset(&request, 0, sizeof(request)); + request.cmd = CMD_PG_SET_STATE; + request.id = id; + request.set_state.state = state; + + memset(&msg, 0, sizeof(msg)); + msg.mrq = MRQ_PG; + msg.tx.data = &request; + msg.tx.size = sizeof(request); + + return tegra_bpmp_transfer(bpmp, &msg); +} + +static int tegra_bpmp_powergate_get_state(struct tegra_bpmp *bpmp, + unsigned int id) +{ + struct mrq_pg_response response; + struct mrq_pg_request request; + struct tegra_bpmp_message msg; + int err; + + memset(&request, 0, sizeof(request)); + request.cmd = CMD_PG_GET_STATE; + request.id = id; + + memset(&response, 0, sizeof(response)); + + memset(&msg, 0, sizeof(msg)); + msg.mrq = MRQ_PG; + msg.tx.data = &request; + msg.tx.size = sizeof(request); + msg.rx.data = &response; + msg.rx.size = sizeof(response); + + err = tegra_bpmp_transfer(bpmp, &msg); + if (err < 0) + return PG_STATE_OFF; + + return response.get_state.state; +} + +static int tegra_bpmp_powergate_get_max_id(struct tegra_bpmp *bpmp) +{ + struct mrq_pg_response response; + struct mrq_pg_request request; + struct tegra_bpmp_message msg; + int err; + + memset(&request, 0, sizeof(request)); + request.cmd = CMD_PG_GET_MAX_ID; + + memset(&response, 0, sizeof(response)); + + memset(&msg, 0, sizeof(msg)); + msg.mrq = MRQ_PG; + msg.tx.data = &request; + msg.tx.size = sizeof(request); + msg.rx.data = &response; + msg.rx.size = sizeof(response); + + err = tegra_bpmp_transfer(bpmp, &msg); + if (err < 0) + return err; + + return response.get_max_id.max_id; +} + +static char *tegra_bpmp_powergate_get_name(struct tegra_bpmp *bpmp, + unsigned int id) +{ + struct mrq_pg_response response; + struct mrq_pg_request request; + struct tegra_bpmp_message msg; + int err; + + memset(&request, 0, sizeof(request)); + request.cmd = CMD_PG_GET_NAME; + request.id = id; + + memset(&response, 0, sizeof(response)); + + memset(&msg, 0, sizeof(msg)); + msg.mrq = MRQ_PG; + msg.tx.data = &request; + msg.tx.size = sizeof(request); + msg.rx.data = &response; + msg.rx.size = sizeof(response); + + err = tegra_bpmp_transfer(bpmp, &msg); + if (err < 0) + return NULL; + + return kstrdup(response.get_name.name, GFP_KERNEL); +} + +static inline bool tegra_bpmp_powergate_is_powered(struct tegra_bpmp *bpmp, + unsigned int id) +{ + return tegra_bpmp_powergate_get_state(bpmp, id) != PG_STATE_OFF; +} + +static int tegra_powergate_power_on(struct generic_pm_domain *domain) +{ + struct tegra_powergate *powergate = to_tegra_powergate(domain); + struct tegra_bpmp *bpmp = powergate->bpmp; + + return tegra_bpmp_powergate_set_state(bpmp, powergate->id, + PG_STATE_ON); +} + +static int tegra_powergate_power_off(struct generic_pm_domain *domain) +{ + struct tegra_powergate *powergate = to_tegra_powergate(domain); + struct tegra_bpmp *bpmp = powergate->bpmp; + + return tegra_bpmp_powergate_set_state(bpmp, powergate->id, + PG_STATE_OFF); +} + +static struct tegra_powergate * +tegra_powergate_add(struct tegra_bpmp *bpmp, + const struct tegra_powergate_info *info) +{ + struct tegra_powergate *powergate; + bool off; + int err; + + off = !tegra_bpmp_powergate_is_powered(bpmp, info->id); + + powergate = devm_kzalloc(bpmp->dev, sizeof(*powergate), GFP_KERNEL); + if (!powergate) + return ERR_PTR(-ENOMEM); + + powergate->id = info->id; + powergate->bpmp = bpmp; + + powergate->genpd.name = kstrdup(info->name, GFP_KERNEL); + powergate->genpd.power_on = tegra_powergate_power_on; + powergate->genpd.power_off = tegra_powergate_power_off; + + err = pm_genpd_init(&powergate->genpd, NULL, off); + if (err < 0) { + kfree(powergate->genpd.name); + return ERR_PTR(err); + } + + return powergate; +} + +static void tegra_powergate_remove(struct tegra_powergate *powergate) +{ + struct generic_pm_domain *genpd = &powergate->genpd; + struct tegra_bpmp *bpmp = powergate->bpmp; + int err; + + err = pm_genpd_remove(genpd); + if (err < 0) + dev_err(bpmp->dev, "failed to remove power domain %s: %d\n", + genpd->name, err); + + kfree(genpd->name); +} + +static int +tegra_bpmp_probe_powergates(struct tegra_bpmp *bpmp, + struct tegra_powergate_info **powergatesp) +{ + struct tegra_powergate_info *powergates; + unsigned int max_id, id, count = 0; + unsigned int num_holes = 0; + int err; + + err = tegra_bpmp_powergate_get_max_id(bpmp); + if (err < 0) + return err; + + max_id = err; + + dev_dbg(bpmp->dev, "maximum powergate ID: %u\n", max_id); + + powergates = kcalloc(max_id + 1, sizeof(*powergates), GFP_KERNEL); + if (!powergates) + return -ENOMEM; + + for (id = 0; id <= max_id; id++) { + struct tegra_powergate_info *info = &powergates[count]; + + info->name = tegra_bpmp_powergate_get_name(bpmp, id); + if (!info->name || info->name[0] == '\0') { + num_holes++; + continue; + } + + info->id = id; + count++; + } + + dev_dbg(bpmp->dev, "holes: %u\n", num_holes); + + *powergatesp = powergates; + + return count; +} + +static int tegra_bpmp_add_powergates(struct tegra_bpmp *bpmp, + struct tegra_powergate_info *powergates, + unsigned int count) +{ + struct genpd_onecell_data *genpd = &bpmp->genpd; + struct generic_pm_domain **domains; + struct tegra_powergate *powergate; + unsigned int i; + int err; + + domains = kcalloc(count, sizeof(*domains), GFP_KERNEL); + if (!domains) + return -ENOMEM; + + for (i = 0; i < count; i++) { + powergate = tegra_powergate_add(bpmp, &powergates[i]); + if (IS_ERR(powergate)) { + err = PTR_ERR(powergate); + goto remove; + } + + dev_dbg(bpmp->dev, "added power domain %s\n", + powergate->genpd.name); + domains[i] = &powergate->genpd; + } + + genpd->num_domains = count; + genpd->domains = domains; + + return 0; + +remove: + while (i--) { + powergate = to_tegra_powergate(domains[i]); + tegra_powergate_remove(powergate); + } + + kfree(genpd->domains); + return err; +} + +static void tegra_bpmp_remove_powergates(struct tegra_bpmp *bpmp) +{ + struct genpd_onecell_data *genpd = &bpmp->genpd; + unsigned int i = genpd->num_domains; + struct tegra_powergate *powergate; + + while (i--) { + dev_dbg(bpmp->dev, "removing power domain %s\n", + genpd->domains[i]->name); + powergate = to_tegra_powergate(genpd->domains[i]); + tegra_powergate_remove(powergate); + } +} + +static struct generic_pm_domain * +tegra_powergate_xlate(struct of_phandle_args *spec, void *data) +{ + struct generic_pm_domain *domain = ERR_PTR(-ENOENT); + struct genpd_onecell_data *genpd = data; + unsigned int i; + + for (i = 0; i < genpd->num_domains; i++) { + struct tegra_powergate *powergate; + + powergate = to_tegra_powergate(genpd->domains[i]); + if (powergate->id == spec->args[0]) { + domain = &powergate->genpd; + break; + } + } + + return domain; +} + +int tegra_bpmp_init_powergates(struct tegra_bpmp *bpmp) +{ + struct device_node *np = bpmp->dev->of_node; + struct tegra_powergate_info *powergates; + struct device *dev = bpmp->dev; + unsigned int count, i; + int err; + + err = tegra_bpmp_probe_powergates(bpmp, &powergates); + if (err < 0) + return err; + + count = err; + + dev_dbg(dev, "%u power domains probed\n", count); + + err = tegra_bpmp_add_powergates(bpmp, powergates, count); + if (err < 0) + goto free; + + bpmp->genpd.xlate = tegra_powergate_xlate; + + err = of_genpd_add_provider_onecell(np, &bpmp->genpd); + if (err < 0) { + dev_err(dev, "failed to add power domain provider: %d\n", err); + tegra_bpmp_remove_powergates(bpmp); + } + +free: + for (i = 0; i < count; i++) + kfree(powergates[i].name); + + kfree(powergates); + return err; +} diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index b7803a251044..41004d97cefa 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -206,9 +206,13 @@ static inline void pm_genpd_syscore_poweron(struct device *dev) {} /* OF PM domain providers */ struct of_device_id; +typedef struct generic_pm_domain *(*genpd_xlate_t)(struct of_phandle_args *args, + void *data); + struct genpd_onecell_data { struct generic_pm_domain **domains; unsigned int num_domains; + genpd_xlate_t xlate; }; #ifdef CONFIG_PM_GENERIC_DOMAINS_OF diff --git a/include/soc/tegra/bpmp-abi.h b/include/soc/tegra/bpmp-abi.h index 0aaef5960e29..98d8d38b99a1 100644 --- a/include/soc/tegra/bpmp-abi.h +++ b/include/soc/tegra/bpmp-abi.h @@ -81,13 +81,18 @@ * Provides the MRQ number for the MRQ message: #mrq. The remainder of * the MRQ message is a payload (immediately following the * mrq_request) whose format depends on mrq. - * - * @todo document the flags */ struct mrq_request { /** @brief MRQ number of the request */ uint32_t mrq; - /** @brief flags for the request */ + /** + * @brief flags providing follow up directions to the receiver + * + * | Bit | Description | + * |-----|--------------------------------------------| + * | 1 | ring the sender's doorbell when responding | + * | 0 | should be 1 | + */ uint32_t flags; } __ABI_PACKED; @@ -99,13 +104,11 @@ struct mrq_request { * remainder of the MRQ response is a payload (immediately following * the mrq_response) whose format depends on the associated * mrq_request::mrq - * - * @todo document the flags */ struct mrq_response { /** @brief error code for the MRQ request itself */ int32_t err; - /** @brief flags for the response */ + /** @brief reserved for future use */ uint32_t flags; } __ABI_PACKED; @@ -147,6 +150,8 @@ struct mrq_response { #define MRQ_ABI_RATCHET 29 #define MRQ_EMC_DVFS_LATENCY 31 #define MRQ_TRACE_ITER 64 +#define MRQ_RINGBUF_CONSOLE 65 +#define MRQ_PG 66 /** @} */ @@ -155,7 +160,7 @@ struct mrq_response { * @brief Maximum MRQ code to be sent by CPU software to * BPMP. Subject to change in future */ -#define MAX_CPU_MRQ_ID 64 +#define MAX_CPU_MRQ_ID 66 /** * @addtogroup MRQ_Payloads Message Payloads @@ -175,6 +180,7 @@ struct mrq_response { * @defgroup Vhint CPU Voltage hint * @defgroup MRQ_Deprecated Deprecated MRQ messages * @defgroup EMC + * @defgroup RingbufConsole * @} */ @@ -637,7 +643,7 @@ struct mrq_debugfs_response { * * Initiators: Any * * Targets: BPMP * * Request Payload: @ref mrq_reset_request - * * Response Payload: N/A + * * Response Payload: @ref mrq_reset_response */ /** @@ -647,6 +653,7 @@ enum mrq_reset_commands { CMD_RESET_ASSERT = 1, CMD_RESET_DEASSERT = 2, CMD_RESET_MODULE = 3, + CMD_RESET_GET_MAX_ID = 4, CMD_RESET_MAX, /* not part of ABI and subject to change */ }; @@ -664,6 +671,38 @@ struct mrq_reset_request { uint32_t reset_id; } __ABI_PACKED; +/** + * @ingroup Reset + * @brief Response for MRQ_RESET sub-command CMD_RESET_GET_MAX_ID. When + * this sub-command is not supported, firmware will return -BPMP_EBADCMD + * in mrq_response::err. + */ +struct cmd_reset_get_max_id_response { + /** @brief max reset id */ + uint32_t max_id; +} __ABI_PACKED; + +/** + * @ingroup Reset + * @brief Response with MRQ_RESET + * + * Each sub-command supported by @ref mrq_reset_request may return + * sub-command-specific data. Some do and some do not as indicated + * in the following table + * + * | sub-command | payload | + * |----------------------|------------------| + * | CMD_RESET_ASSERT | - | + * | CMD_RESET_DEASSERT | - | + * | CMD_RESET_MODULE | - | + * | CMD_RESET_GET_MAX_ID | reset_get_max_id | + */ +struct mrq_reset_response { + union { + struct cmd_reset_get_max_id_response reset_get_max_id; + } __UNION_ANON; +} __ABI_PACKED; + /** * @ingroup MRQ_Codes * @def MRQ_I2C @@ -812,6 +851,17 @@ enum { }; /** @} */ +/** + * @name MRQ_CLK properties + * Flag bits for cmd_clk_properties_response::flags and + * cmd_clk_get_all_info_response::flags + * @{ + */ +#define BPMP_CLK_HAS_MUX (1 << 0) +#define BPMP_CLK_HAS_SET_RATE (1 << 1) +#define BPMP_CLK_IS_ROOT (1 << 2) +/** @} */ + #define MRQ_CLK_NAME_MAXLEN 40 #define MRQ_CLK_MAX_PARENTS 16 @@ -1010,7 +1060,7 @@ struct mrq_clk_response { * * * Platforms: All * * Initiators: Any - * * Targets: Any + * * Targets: Any except DMCE * * Request Payload: @ref mrq_query_abi_request * * Response Payload: @ref mrq_query_abi_response */ @@ -1030,6 +1080,9 @@ struct mrq_query_abi_request { /** * @ingroup ABI_info * @brief response to MRQ_QUERY_ABI + * + * @note mrq_response::err of 0 indicates that the query was + * successful, not that the MRQ itself is supported! */ struct mrq_query_abi_response { /** @brief 0 if queried MRQ is supported. Else, -#BPMP_ENODEV */ @@ -1080,7 +1133,9 @@ struct mrq_pg_read_state_response { /** * @ingroup MRQ_Codes * @def MRQ_PG_UPDATE_STATE - * @brief modify the power-gating state of a partition + * @brief modify the power-gating state of a partition. In contrast to + * MRQ_PG calls, the operations that change state (on/off) of power + * partition are reference counted. * * * Platforms: T186 * * Initiators: Any @@ -1124,6 +1179,171 @@ struct mrq_pg_update_state_request { } __ABI_PACKED; /** @} */ +/** + * @ingroup MRQ_Codes + * @def MRQ_PG + * @brief Control power-gating state of a partition. In contrast to + * MRQ_PG_UPDATE_STATE, operations that change the power partition + * state are NOT reference counted + * + * * Platforms: T186 + * * Initiators: Any + * * Targets: BPMP + * * Request Payload: @ref mrq_pg_request + * * Response Payload: @ref mrq_pg_response + * @addtogroup Powergating + * @{ + */ + +/** + * @name MRQ_PG sub-commands + * @{ + */ +enum mrq_pg_cmd { + /** + * @brief Check whether the BPMP driver supports the specified + * request type + * + * mrq_response::err is 0 if the specified request is + * supported and -#BPMP_ENODEV otherwise. + */ + CMD_PG_QUERY_ABI = 0, + + /** + * @brief Set the current state of specified power domain. The + * possible values for power domains are defined in enum + * pg_states + * + * mrq_response:err is + * 0: Success + * -#BPMP_EINVAL: Invalid request parameters + */ + CMD_PG_SET_STATE = 1, + + /** + * @brief Get the current state of specified power domain. The + * possible values for power domains are defined in enum + * pg_states + * + * mrq_response:err is + * 0: Success + * -#BPMP_EINVAL: Invalid request parameters + */ + CMD_PG_GET_STATE = 2, + + /** + * @brief get the name string of specified power domain id. + * + * mrq_response:err is + * 0: Success + * -#BPMP_EINVAL: Invalid request parameters + */ + CMD_PG_GET_NAME = 3, + + + /** + * @brief get the highest power domain id in the system. Not + * all IDs between 0 and max_id are valid IDs. + * + * mrq_response:err is + * 0: Success + * -#BPMP_EINVAL: Invalid request parameters + */ + CMD_PG_GET_MAX_ID = 4, +}; +/** @} */ + +#define MRQ_PG_NAME_MAXLEN 40 + +/** + * @brief possible power domain states in + * cmd_pg_set_state_request:state and cmd_pg_get_state_response:state. + * PG_STATE_OFF: power domain is OFF + * PG_STATE_ON: power domain is ON + * PG_STATE_RUNNING: power domain is ON and made into directly usable + * state by turning on the clocks associated with + * the domain + */ +enum pg_states { + PG_STATE_OFF = 0, + PG_STATE_ON = 1, + PG_STATE_RUNNING = 2, +}; + +struct cmd_pg_query_abi_request { + uint32_t type; /* enum mrq_pg_cmd */ +} __ABI_PACKED; + +struct cmd_pg_set_state_request { + uint32_t state; /* enum pg_states */ +} __ABI_PACKED; + +struct cmd_pg_get_state_response { + uint32_t state; /* enum pg_states */ +} __ABI_PACKED; + +struct cmd_pg_get_name_response { + uint8_t name[MRQ_PG_NAME_MAXLEN]; +} __ABI_PACKED; + +struct cmd_pg_get_max_id_response { + uint32_t max_id; +} __ABI_PACKED; + +/** + * @ingroup Powergating + * @brief request with #MRQ_PG + * + * Used by the sender of an #MRQ_PG message to control power + * partitions. The pg_request is split into several sub-commands. Some + * sub-commands require no additional data. Others have a sub-command + * specific payload + * + * |sub-command |payload | + * |----------------------------|-----------------------| + * |CMD_PG_QUERY_ABI | query_abi | + * |CMD_PG_SET_STATE | set_state | + * |CMD_PG_GET_STATE | - | + * |CMD_PG_GET_NAME | - | + * |CMD_PG_GET_MAX_ID | - | + * + */ + +struct mrq_pg_request { + uint32_t cmd; + uint32_t id; + union { + struct cmd_pg_query_abi_request query_abi; + struct cmd_pg_set_state_request set_state; + } __UNION_ANON; +} __ABI_PACKED; + +/** + * @ingroup Powergating + * @brief response to MRQ_PG + * + * Each sub-command supported by @ref mrq_pg_request may return + * sub-command-specific data. Some do and some do not as indicated in + * the following table + * + * |sub-command |payload | + * |----------------------------|-----------------------| + * |CMD_PG_QUERY_ABI | - | + * |CMD_PG_SET_STATE | - | + * |CMD_PG_GET_STATE | get_state | + * |CMD_PG_GET_NAME | get_name | + * |CMD_PG_GET_MAX_ID | get_max_id | + * + */ + +struct mrq_pg_response { + union { + struct cmd_pg_get_state_response get_state; + struct cmd_pg_get_name_response get_name; + struct cmd_pg_get_max_id_response get_max_id; + } __UNION_ANON; +} __ABI_PACKED; + /** * @ingroup MRQ_Codes * @def MRQ_THERMAL @@ -1529,6 +1749,184 @@ struct mrq_trace_iter_request { /** @} */ +/** + * @ingroup MRQ_Codes + * @def MRQ_RINGBUF_CONSOLE + * @brief A ring buffer debug console for BPMP + * @addtogroup RingbufConsole + * + * The ring buffer debug console aims to be a substitute for the UART debug + * console. The debug console is implemented with two ring buffers in the + * BPMP-FW, the RX (receive) and TX (transmit) buffers. Characters can be read + * and written to the buffers by the host via the MRQ interface. + * + * @{ + */ + +/** + * @brief Maximum number of bytes transferred in a single write command to the + * BPMP + * + * This is determined by the number of free bytes in the message struct, + * rounded down to a multiple of four. + */ +#define MRQ_RINGBUF_CONSOLE_MAX_WRITE_LEN 112 + +/** + * @brief Maximum number of bytes transferred in a single read command to the + * BPMP + * + * This is determined by the number of free bytes in the message struct, + * rounded down to a multiple of four. + */ +#define MRQ_RINGBUF_CONSOLE_MAX_READ_LEN 116 + +enum mrq_ringbuf_console_host_to_bpmp_cmd { + /** + * @brief Check whether the BPMP driver supports the specified request + * type + * + * mrq_response::err is 0 if the specified request is supported and + * -#BPMP_ENODEV otherwise + */ + CMD_RINGBUF_CONSOLE_QUERY_ABI = 0, + /** + * @brief Perform a read operation on the BPMP TX buffer + * + * mrq_response::err is 0 + */ + CMD_RINGBUF_CONSOLE_READ = 1, + /** + * @brief Perform a write operation on the BPMP RX buffer + * + * mrq_response::err is 0 if the operation was successful and + * -#BPMP_ENODEV otherwise + */ + CMD_RINGBUF_CONSOLE_WRITE = 2, + /** + * @brief Get the length of the buffer and the physical addresses of + * the buffer data and the head and tail counters + * + * mrq_response::err is 0 if the operation was successful and + * -#BPMP_ENODEV otherwise + */ + CMD_RINGBUF_CONSOLE_GET_FIFO = 3, +}; + +/** + * @ingroup RingbufConsole + * @brief Host->BPMP request data for request type + * #CMD_RINGBUF_CONSOLE_QUERY_ABI + */ +struct cmd_ringbuf_console_query_abi_req { + /** @brief Command identifier to be queried */ + uint32_t cmd; +} __ABI_PACKED; + +/** @private */ +struct cmd_ringbuf_console_query_abi_resp { + EMPTY +} __ABI_PACKED; + +/** + * @ingroup RingbufConsole + * @brief Host->BPMP request data for request type #CMD_RINGBUF_CONSOLE_READ + */ +struct cmd_ringbuf_console_read_req { + /** + * @brief Number of bytes requested to be read from the BPMP TX buffer + */ + uint8_t len; +} __ABI_PACKED; + +/** + * @ingroup RingbufConsole + * @brief BPMP->Host response data for request type #CMD_RINGBUF_CONSOLE_READ + */ +struct cmd_ringbuf_console_read_resp { + /** @brief The actual data read from the BPMP TX buffer */ + uint8_t data[MRQ_RINGBUF_CONSOLE_MAX_READ_LEN]; + /** @brief Number of bytes in cmd_ringbuf_console_read_resp::data */ + uint8_t len; +} __ABI_PACKED; + +/** + * @ingroup RingbufConsole + * @brief Host->BPMP request data for request type #CMD_RINGBUF_CONSOLE_WRITE + */ +struct cmd_ringbuf_console_write_req { + /** @brief The actual data to be written to the BPMP RX buffer */ + uint8_t data[MRQ_RINGBUF_CONSOLE_MAX_WRITE_LEN]; + /** @brief Number of bytes in cmd_ringbuf_console_write_req::data */ + uint8_t len; +} __ABI_PACKED; + +/** + * @ingroup RingbufConsole + * @brief BPMP->Host response data for request type #CMD_RINGBUF_CONSOLE_WRITE + */ +struct cmd_ringbuf_console_write_resp { + /** @brief Number of bytes of available space in the BPMP RX buffer */ + uint32_t space_avail; + /** @brief Number of bytes that were written to the BPMP RX buffer */ + uint8_t len; +} __ABI_PACKED; + +/** @private */ +struct cmd_ringbuf_console_get_fifo_req { + EMPTY +} __ABI_PACKED; + +/** + * @ingroup RingbufConsole + * @brief BPMP->Host reply data for request type #CMD_RINGBUF_CONSOLE_GET_FIFO + */ +struct cmd_ringbuf_console_get_fifo_resp { + /** @brief Physical address of the BPMP TX buffer */ + uint64_t bpmp_tx_buf_addr; + /** @brief Physical address of the BPMP TX buffer head counter */ + uint64_t bpmp_tx_head_addr; + /** @brief Physical address of the BPMP TX buffer tail counter */ + uint64_t bpmp_tx_tail_addr; + /** @brief Length of the BPMP TX buffer */ + uint32_t bpmp_tx_buf_len; +} __ABI_PACKED; + +/** + * @ingroup RingbufConsole + * @brief Host->BPMP request data. + * + * Reply type is union #mrq_ringbuf_console_bpmp_to_host_response . + */ +struct mrq_ringbuf_console_host_to_bpmp_request { + /** + * @brief type of request. Values listed in enum + * #mrq_ringbuf_console_host_to_bpmp_cmd. + */ + uint32_t type; + /** @brief request type specific parameters. */ + union { + struct cmd_ringbuf_console_query_abi_req query_abi; + struct cmd_ringbuf_console_read_req read; + struct cmd_ringbuf_console_write_req write; + struct cmd_ringbuf_console_get_fifo_req get_fifo; + } __UNION_ANON; +} __ABI_PACKED; + +/** + * @ingroup RingbufConsole + * @brief Host->BPMP reply data + * + * In response to struct #mrq_ringbuf_console_host_to_bpmp_request. + */ +union mrq_ringbuf_console_bpmp_to_host_response { + struct cmd_ringbuf_console_query_abi_resp query_abi; + struct cmd_ringbuf_console_read_resp read; + struct cmd_ringbuf_console_write_resp write; + struct cmd_ringbuf_console_get_fifo_resp get_fifo; +} __ABI_PACKED; +/** @} */ + /* * 4. Enumerations */ diff --git a/include/soc/tegra/bpmp.h b/include/soc/tegra/bpmp.h index 13dcd44e91bb..9ba65222bd3f 100644 --- a/include/soc/tegra/bpmp.h +++ b/include/soc/tegra/bpmp.h @@ -15,6 +15,7 @@ #define __SOC_TEGRA_BPMP_H #include +#include #include #include #include @@ -91,6 +92,8 @@ struct tegra_bpmp { unsigned int num_clocks; struct reset_controller_dev rstc; + + struct genpd_onecell_data genpd; }; struct tegra_bpmp *tegra_bpmp_get(struct device *dev); @@ -138,4 +141,13 @@ static inline int tegra_bpmp_init_resets(struct tegra_bpmp *bpmp) } #endif +#if IS_ENABLED(CONFIG_SOC_TEGRA_POWERGATE_BPMP) +int tegra_bpmp_init_powergates(struct tegra_bpmp *bpmp); +#else +static inline int tegra_bpmp_init_powergates(struct tegra_bpmp *bpmp) +{ + return 0; +} +#endif + #endif /* __SOC_TEGRA_BPMP_H */