diff options
Diffstat (limited to 'drivers/firmware')
| -rw-r--r-- | drivers/firmware/imx/misc.c | 38 | ||||
| -rw-r--r-- | drivers/firmware/imx/scu-pd.c | 1 | ||||
| -rw-r--r-- | drivers/firmware/raspberrypi.c | 11 | ||||
| -rw-r--r-- | drivers/firmware/tegra/Makefile | 3 | ||||
| -rw-r--r-- | drivers/firmware/tegra/bpmp-private.h | 34 | ||||
| -rw-r--r-- | drivers/firmware/tegra/bpmp-tegra186.c | 305 | ||||
| -rw-r--r-- | drivers/firmware/tegra/bpmp-tegra210.c | 243 | ||||
| -rw-r--r-- | drivers/firmware/tegra/bpmp.c | 376 | ||||
| -rw-r--r-- | drivers/firmware/ti_sci.c | 21 | ||||
| -rw-r--r-- | drivers/firmware/xilinx/Kconfig | 1 | ||||
| -rw-r--r-- | drivers/firmware/xilinx/zynqmp.c | 166 |
11 files changed, 954 insertions, 245 deletions
diff --git a/drivers/firmware/imx/misc.c b/drivers/firmware/imx/misc.c index 97f5424dbac9..4b56a587dacd 100644 --- a/drivers/firmware/imx/misc.c +++ b/drivers/firmware/imx/misc.c | |||
| @@ -18,6 +18,14 @@ struct imx_sc_msg_req_misc_set_ctrl { | |||
| 18 | u16 resource; | 18 | u16 resource; |
| 19 | } __packed; | 19 | } __packed; |
| 20 | 20 | ||
| 21 | struct imx_sc_msg_req_cpu_start { | ||
| 22 | struct imx_sc_rpc_msg hdr; | ||
| 23 | u32 address_hi; | ||
| 24 | u32 address_lo; | ||
| 25 | u16 resource; | ||
| 26 | u8 enable; | ||
| 27 | } __packed; | ||
| 28 | |||
| 21 | struct imx_sc_msg_req_misc_get_ctrl { | 29 | struct imx_sc_msg_req_misc_get_ctrl { |
| 22 | struct imx_sc_rpc_msg hdr; | 30 | struct imx_sc_rpc_msg hdr; |
| 23 | u32 ctrl; | 31 | u32 ctrl; |
| @@ -97,3 +105,33 @@ int imx_sc_misc_get_control(struct imx_sc_ipc *ipc, u32 resource, | |||
| 97 | return 0; | 105 | return 0; |
| 98 | } | 106 | } |
| 99 | EXPORT_SYMBOL(imx_sc_misc_get_control); | 107 | EXPORT_SYMBOL(imx_sc_misc_get_control); |
| 108 | |||
| 109 | /* | ||
| 110 | * This function starts/stops a CPU identified by @resource | ||
| 111 | * | ||
| 112 | * @param[in] ipc IPC handle | ||
| 113 | * @param[in] resource resource the control is associated with | ||
| 114 | * @param[in] enable true for start, false for stop | ||
| 115 | * @param[in] phys_addr initial instruction address to be executed | ||
| 116 | * | ||
| 117 | * @return Returns 0 for success and < 0 for errors. | ||
| 118 | */ | ||
| 119 | int imx_sc_pm_cpu_start(struct imx_sc_ipc *ipc, u32 resource, | ||
| 120 | bool enable, u64 phys_addr) | ||
| 121 | { | ||
| 122 | struct imx_sc_msg_req_cpu_start msg; | ||
| 123 | struct imx_sc_rpc_msg *hdr = &msg.hdr; | ||
| 124 | |||
| 125 | hdr->ver = IMX_SC_RPC_VERSION; | ||
| 126 | hdr->svc = IMX_SC_RPC_SVC_PM; | ||
| 127 | hdr->func = IMX_SC_PM_FUNC_CPU_START; | ||
| 128 | hdr->size = 4; | ||
| 129 | |||
| 130 | msg.address_hi = phys_addr >> 32; | ||
| 131 | msg.address_lo = phys_addr; | ||
| 132 | msg.resource = resource; | ||
| 133 | msg.enable = enable; | ||
| 134 | |||
| 135 | return imx_scu_call_rpc(ipc, &msg, true); | ||
| 136 | } | ||
| 137 | EXPORT_SYMBOL(imx_sc_pm_cpu_start); | ||
diff --git a/drivers/firmware/imx/scu-pd.c b/drivers/firmware/imx/scu-pd.c index 407245f2efd0..39a94c7177fc 100644 --- a/drivers/firmware/imx/scu-pd.c +++ b/drivers/firmware/imx/scu-pd.c | |||
| @@ -322,6 +322,7 @@ static int imx_sc_pd_probe(struct platform_device *pdev) | |||
| 322 | 322 | ||
| 323 | static const struct of_device_id imx_sc_pd_match[] = { | 323 | static const struct of_device_id imx_sc_pd_match[] = { |
| 324 | { .compatible = "fsl,imx8qxp-scu-pd", &imx8qxp_scu_pd}, | 324 | { .compatible = "fsl,imx8qxp-scu-pd", &imx8qxp_scu_pd}, |
| 325 | { .compatible = "fsl,scu-pd", &imx8qxp_scu_pd}, | ||
| 325 | { /* sentinel */ } | 326 | { /* sentinel */ } |
| 326 | }; | 327 | }; |
| 327 | 328 | ||
diff --git a/drivers/firmware/raspberrypi.c b/drivers/firmware/raspberrypi.c index a13558154ac3..61be15d9df7d 100644 --- a/drivers/firmware/raspberrypi.c +++ b/drivers/firmware/raspberrypi.c | |||
| @@ -238,6 +238,16 @@ static int rpi_firmware_probe(struct platform_device *pdev) | |||
| 238 | return 0; | 238 | return 0; |
| 239 | } | 239 | } |
| 240 | 240 | ||
| 241 | static void rpi_firmware_shutdown(struct platform_device *pdev) | ||
| 242 | { | ||
| 243 | struct rpi_firmware *fw = platform_get_drvdata(pdev); | ||
| 244 | |||
| 245 | if (!fw) | ||
| 246 | return; | ||
| 247 | |||
| 248 | rpi_firmware_property(fw, RPI_FIRMWARE_NOTIFY_REBOOT, NULL, 0); | ||
| 249 | } | ||
| 250 | |||
| 241 | static int rpi_firmware_remove(struct platform_device *pdev) | 251 | static int rpi_firmware_remove(struct platform_device *pdev) |
| 242 | { | 252 | { |
| 243 | struct rpi_firmware *fw = platform_get_drvdata(pdev); | 253 | struct rpi_firmware *fw = platform_get_drvdata(pdev); |
| @@ -278,6 +288,7 @@ static struct platform_driver rpi_firmware_driver = { | |||
| 278 | .of_match_table = rpi_firmware_of_match, | 288 | .of_match_table = rpi_firmware_of_match, |
| 279 | }, | 289 | }, |
| 280 | .probe = rpi_firmware_probe, | 290 | .probe = rpi_firmware_probe, |
| 291 | .shutdown = rpi_firmware_shutdown, | ||
| 281 | .remove = rpi_firmware_remove, | 292 | .remove = rpi_firmware_remove, |
| 282 | }; | 293 | }; |
| 283 | module_platform_driver(rpi_firmware_driver); | 294 | module_platform_driver(rpi_firmware_driver); |
diff --git a/drivers/firmware/tegra/Makefile b/drivers/firmware/tegra/Makefile index 1b826dcca719..676b01caff05 100644 --- a/drivers/firmware/tegra/Makefile +++ b/drivers/firmware/tegra/Makefile | |||
| @@ -1,4 +1,7 @@ | |||
| 1 | tegra-bpmp-y = bpmp.o | 1 | tegra-bpmp-y = bpmp.o |
| 2 | tegra-bpmp-$(CONFIG_ARCH_TEGRA_210_SOC) += bpmp-tegra210.o | ||
| 3 | tegra-bpmp-$(CONFIG_ARCH_TEGRA_186_SOC) += bpmp-tegra186.o | ||
| 4 | tegra-bpmp-$(CONFIG_ARCH_TEGRA_194_SOC) += bpmp-tegra186.o | ||
| 2 | tegra-bpmp-$(CONFIG_DEBUG_FS) += bpmp-debugfs.o | 5 | tegra-bpmp-$(CONFIG_DEBUG_FS) += bpmp-debugfs.o |
| 3 | obj-$(CONFIG_TEGRA_BPMP) += tegra-bpmp.o | 6 | obj-$(CONFIG_TEGRA_BPMP) += tegra-bpmp.o |
| 4 | obj-$(CONFIG_TEGRA_IVC) += ivc.o | 7 | obj-$(CONFIG_TEGRA_IVC) += ivc.o |
diff --git a/drivers/firmware/tegra/bpmp-private.h b/drivers/firmware/tegra/bpmp-private.h new file mode 100644 index 000000000000..54d560c48398 --- /dev/null +++ b/drivers/firmware/tegra/bpmp-private.h | |||
| @@ -0,0 +1,34 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 2 | /* | ||
| 3 | * Copyright (c) 2018, NVIDIA CORPORATION. | ||
| 4 | */ | ||
| 5 | |||
| 6 | #ifndef __FIRMWARE_TEGRA_BPMP_PRIVATE_H | ||
| 7 | #define __FIRMWARE_TEGRA_BPMP_PRIVATE_H | ||
| 8 | |||
| 9 | #include <soc/tegra/bpmp.h> | ||
| 10 | |||
| 11 | struct tegra_bpmp_ops { | ||
| 12 | int (*init)(struct tegra_bpmp *bpmp); | ||
| 13 | void (*deinit)(struct tegra_bpmp *bpmp); | ||
| 14 | bool (*is_response_ready)(struct tegra_bpmp_channel *channel); | ||
| 15 | bool (*is_request_ready)(struct tegra_bpmp_channel *channel); | ||
| 16 | int (*ack_response)(struct tegra_bpmp_channel *channel); | ||
| 17 | int (*ack_request)(struct tegra_bpmp_channel *channel); | ||
| 18 | bool (*is_response_channel_free)(struct tegra_bpmp_channel *channel); | ||
| 19 | bool (*is_request_channel_free)(struct tegra_bpmp_channel *channel); | ||
| 20 | int (*post_response)(struct tegra_bpmp_channel *channel); | ||
| 21 | int (*post_request)(struct tegra_bpmp_channel *channel); | ||
| 22 | int (*ring_doorbell)(struct tegra_bpmp *bpmp); | ||
| 23 | int (*resume)(struct tegra_bpmp *bpmp); | ||
| 24 | }; | ||
| 25 | |||
| 26 | #if IS_ENABLED(CONFIG_ARCH_TEGRA_186_SOC) || \ | ||
| 27 | IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC) | ||
| 28 | extern const struct tegra_bpmp_ops tegra186_bpmp_ops; | ||
| 29 | #endif | ||
| 30 | #if IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC) | ||
| 31 | extern const struct tegra_bpmp_ops tegra210_bpmp_ops; | ||
| 32 | #endif | ||
| 33 | |||
| 34 | #endif | ||
diff --git a/drivers/firmware/tegra/bpmp-tegra186.c b/drivers/firmware/tegra/bpmp-tegra186.c new file mode 100644 index 000000000000..ea308751635f --- /dev/null +++ b/drivers/firmware/tegra/bpmp-tegra186.c | |||
| @@ -0,0 +1,305 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* | ||
| 3 | * Copyright (c) 2018, NVIDIA CORPORATION. | ||
| 4 | */ | ||
| 5 | |||
| 6 | #include <linux/genalloc.h> | ||
| 7 | #include <linux/mailbox_client.h> | ||
| 8 | #include <linux/platform_device.h> | ||
| 9 | |||
| 10 | #include <soc/tegra/bpmp.h> | ||
| 11 | #include <soc/tegra/bpmp-abi.h> | ||
| 12 | #include <soc/tegra/ivc.h> | ||
| 13 | |||
| 14 | #include "bpmp-private.h" | ||
| 15 | |||
| 16 | struct tegra186_bpmp { | ||
| 17 | struct tegra_bpmp *parent; | ||
| 18 | |||
| 19 | struct { | ||
| 20 | struct gen_pool *pool; | ||
| 21 | dma_addr_t phys; | ||
| 22 | void *virt; | ||
| 23 | } tx, rx; | ||
| 24 | |||
| 25 | struct { | ||
| 26 | struct mbox_client client; | ||
| 27 | struct mbox_chan *channel; | ||
| 28 | } mbox; | ||
| 29 | }; | ||
| 30 | |||
| 31 | static inline struct tegra_bpmp * | ||
| 32 | mbox_client_to_bpmp(struct mbox_client *client) | ||
| 33 | { | ||
| 34 | struct tegra186_bpmp *priv; | ||
| 35 | |||
| 36 | priv = container_of(client, struct tegra186_bpmp, mbox.client); | ||
| 37 | |||
| 38 | return priv->parent; | ||
| 39 | } | ||
| 40 | |||
| 41 | static bool tegra186_bpmp_is_message_ready(struct tegra_bpmp_channel *channel) | ||
| 42 | { | ||
| 43 | void *frame; | ||
| 44 | |||
| 45 | frame = tegra_ivc_read_get_next_frame(channel->ivc); | ||
| 46 | if (IS_ERR(frame)) { | ||
| 47 | channel->ib = NULL; | ||
| 48 | return false; | ||
| 49 | } | ||
| 50 | |||
| 51 | channel->ib = frame; | ||
| 52 | |||
| 53 | return true; | ||
| 54 | } | ||
| 55 | |||
| 56 | static bool tegra186_bpmp_is_channel_free(struct tegra_bpmp_channel *channel) | ||
| 57 | { | ||
| 58 | void *frame; | ||
| 59 | |||
| 60 | frame = tegra_ivc_write_get_next_frame(channel->ivc); | ||
| 61 | if (IS_ERR(frame)) { | ||
| 62 | channel->ob = NULL; | ||
| 63 | return false; | ||
| 64 | } | ||
| 65 | |||
| 66 | channel->ob = frame; | ||
| 67 | |||
| 68 | return true; | ||
| 69 | } | ||
| 70 | |||
| 71 | static int tegra186_bpmp_ack_message(struct tegra_bpmp_channel *channel) | ||
| 72 | { | ||
| 73 | return tegra_ivc_read_advance(channel->ivc); | ||
| 74 | } | ||
| 75 | |||
| 76 | static int tegra186_bpmp_post_message(struct tegra_bpmp_channel *channel) | ||
| 77 | { | ||
| 78 | return tegra_ivc_write_advance(channel->ivc); | ||
| 79 | } | ||
| 80 | |||
| 81 | static int tegra186_bpmp_ring_doorbell(struct tegra_bpmp *bpmp) | ||
| 82 | { | ||
| 83 | struct tegra186_bpmp *priv = bpmp->priv; | ||
| 84 | int err; | ||
| 85 | |||
| 86 | err = mbox_send_message(priv->mbox.channel, NULL); | ||
| 87 | if (err < 0) | ||
| 88 | return err; | ||
| 89 | |||
| 90 | mbox_client_txdone(priv->mbox.channel, 0); | ||
| 91 | |||
| 92 | return 0; | ||
| 93 | } | ||
| 94 | |||
| 95 | static void tegra186_bpmp_ivc_notify(struct tegra_ivc *ivc, void *data) | ||
| 96 | { | ||
| 97 | struct tegra_bpmp *bpmp = data; | ||
| 98 | struct tegra186_bpmp *priv = bpmp->priv; | ||
| 99 | |||
| 100 | if (WARN_ON(priv->mbox.channel == NULL)) | ||
| 101 | return; | ||
| 102 | |||
| 103 | tegra186_bpmp_ring_doorbell(bpmp); | ||
| 104 | } | ||
| 105 | |||
| 106 | static int tegra186_bpmp_channel_init(struct tegra_bpmp_channel *channel, | ||
| 107 | struct tegra_bpmp *bpmp, | ||
| 108 | unsigned int index) | ||
| 109 | { | ||
| 110 | struct tegra186_bpmp *priv = bpmp->priv; | ||
| 111 | size_t message_size, queue_size; | ||
| 112 | unsigned int offset; | ||
| 113 | int err; | ||
| 114 | |||
| 115 | channel->ivc = devm_kzalloc(bpmp->dev, sizeof(*channel->ivc), | ||
| 116 | GFP_KERNEL); | ||
| 117 | if (!channel->ivc) | ||
| 118 | return -ENOMEM; | ||
| 119 | |||
| 120 | message_size = tegra_ivc_align(MSG_MIN_SZ); | ||
| 121 | queue_size = tegra_ivc_total_queue_size(message_size); | ||
| 122 | offset = queue_size * index; | ||
| 123 | |||
| 124 | err = tegra_ivc_init(channel->ivc, NULL, | ||
| 125 | priv->rx.virt + offset, priv->rx.phys + offset, | ||
| 126 | priv->tx.virt + offset, priv->tx.phys + offset, | ||
| 127 | 1, message_size, tegra186_bpmp_ivc_notify, | ||
| 128 | bpmp); | ||
| 129 | if (err < 0) { | ||
| 130 | dev_err(bpmp->dev, "failed to setup IVC for channel %u: %d\n", | ||
| 131 | index, err); | ||
| 132 | return err; | ||
| 133 | } | ||
| 134 | |||
| 135 | init_completion(&channel->completion); | ||
| 136 | channel->bpmp = bpmp; | ||
| 137 | |||
| 138 | return 0; | ||
| 139 | } | ||
| 140 | |||
| 141 | static void tegra186_bpmp_channel_reset(struct tegra_bpmp_channel *channel) | ||
| 142 | { | ||
| 143 | /* reset the channel state */ | ||
| 144 | tegra_ivc_reset(channel->ivc); | ||
| 145 | |||
| 146 | /* sync the channel state with BPMP */ | ||
| 147 | while (tegra_ivc_notified(channel->ivc)) | ||
| 148 | ; | ||
| 149 | } | ||
| 150 | |||
| 151 | static void tegra186_bpmp_channel_cleanup(struct tegra_bpmp_channel *channel) | ||
| 152 | { | ||
| 153 | tegra_ivc_cleanup(channel->ivc); | ||
| 154 | } | ||
| 155 | |||
| 156 | static void mbox_handle_rx(struct mbox_client *client, void *data) | ||
| 157 | { | ||
| 158 | struct tegra_bpmp *bpmp = mbox_client_to_bpmp(client); | ||
| 159 | |||
| 160 | tegra_bpmp_handle_rx(bpmp); | ||
| 161 | } | ||
| 162 | |||
| 163 | static int tegra186_bpmp_init(struct tegra_bpmp *bpmp) | ||
| 164 | { | ||
| 165 | struct tegra186_bpmp *priv; | ||
| 166 | unsigned int i; | ||
| 167 | int err; | ||
| 168 | |||
| 169 | priv = devm_kzalloc(bpmp->dev, sizeof(*priv), GFP_KERNEL); | ||
| 170 | if (!priv) | ||
| 171 | return -ENOMEM; | ||
| 172 | |||
| 173 | bpmp->priv = priv; | ||
| 174 | priv->parent = bpmp; | ||
| 175 | |||
| 176 | priv->tx.pool = of_gen_pool_get(bpmp->dev->of_node, "shmem", 0); | ||
| 177 | if (!priv->tx.pool) { | ||
| 178 | dev_err(bpmp->dev, "TX shmem pool not found\n"); | ||
| 179 | return -ENOMEM; | ||
| 180 | } | ||
| 181 | |||
| 182 | priv->tx.virt = gen_pool_dma_alloc(priv->tx.pool, 4096, &priv->tx.phys); | ||
| 183 | if (!priv->tx.virt) { | ||
| 184 | dev_err(bpmp->dev, "failed to allocate from TX pool\n"); | ||
| 185 | return -ENOMEM; | ||
| 186 | } | ||
| 187 | |||
| 188 | priv->rx.pool = of_gen_pool_get(bpmp->dev->of_node, "shmem", 1); | ||
| 189 | if (!priv->rx.pool) { | ||
| 190 | dev_err(bpmp->dev, "RX shmem pool not found\n"); | ||
| 191 | err = -ENOMEM; | ||
| 192 | goto free_tx; | ||
| 193 | } | ||
| 194 | |||
| 195 | priv->rx.virt = gen_pool_dma_alloc(priv->rx.pool, 4096, &priv->rx.phys); | ||
| 196 | if (!priv->rx.virt) { | ||
| 197 | dev_err(bpmp->dev, "failed to allocate from RX pool\n"); | ||
| 198 | err = -ENOMEM; | ||
| 199 | goto free_tx; | ||
| 200 | } | ||
| 201 | |||
| 202 | err = tegra186_bpmp_channel_init(bpmp->tx_channel, bpmp, | ||
| 203 | bpmp->soc->channels.cpu_tx.offset); | ||
| 204 | if (err < 0) | ||
| 205 | goto free_rx; | ||
| 206 | |||
| 207 | err = tegra186_bpmp_channel_init(bpmp->rx_channel, bpmp, | ||
| 208 | bpmp->soc->channels.cpu_rx.offset); | ||
| 209 | if (err < 0) | ||
| 210 | goto cleanup_tx_channel; | ||
| 211 | |||
| 212 | for (i = 0; i < bpmp->threaded.count; i++) { | ||
| 213 | unsigned int index = bpmp->soc->channels.thread.offset + i; | ||
| 214 | |||
| 215 | err = tegra186_bpmp_channel_init(&bpmp->threaded_channels[i], | ||
| 216 | bpmp, index); | ||
| 217 | if (err < 0) | ||
| 218 | goto cleanup_channels; | ||
| 219 | } | ||
| 220 | |||
| 221 | /* mbox registration */ | ||
| 222 | priv->mbox.client.dev = bpmp->dev; | ||
| 223 | priv->mbox.client.rx_callback = mbox_handle_rx; | ||
| 224 | priv->mbox.client.tx_block = false; | ||
| 225 | priv->mbox.client.knows_txdone = false; | ||
| 226 | |||
| 227 | priv->mbox.channel = mbox_request_channel(&priv->mbox.client, 0); | ||
| 228 | if (IS_ERR(priv->mbox.channel)) { | ||
| 229 | err = PTR_ERR(priv->mbox.channel); | ||
| 230 | dev_err(bpmp->dev, "failed to get HSP mailbox: %d\n", err); | ||
| 231 | goto cleanup_channels; | ||
| 232 | } | ||
| 233 | |||
| 234 | tegra186_bpmp_channel_reset(bpmp->tx_channel); | ||
| 235 | tegra186_bpmp_channel_reset(bpmp->rx_channel); | ||
| 236 | |||
| 237 | for (i = 0; i < bpmp->threaded.count; i++) | ||
| 238 | tegra186_bpmp_channel_reset(&bpmp->threaded_channels[i]); | ||
| 239 | |||
| 240 | return 0; | ||
| 241 | |||
| 242 | cleanup_channels: | ||
| 243 | for (i = 0; i < bpmp->threaded.count; i++) { | ||
| 244 | if (!bpmp->threaded_channels[i].bpmp) | ||
| 245 | continue; | ||
| 246 | |||
| 247 | tegra186_bpmp_channel_cleanup(&bpmp->threaded_channels[i]); | ||
| 248 | } | ||
| 249 | |||
| 250 | tegra186_bpmp_channel_cleanup(bpmp->rx_channel); | ||
| 251 | cleanup_tx_channel: | ||
| 252 | tegra186_bpmp_channel_cleanup(bpmp->tx_channel); | ||
| 253 | free_rx: | ||
| 254 | gen_pool_free(priv->rx.pool, (unsigned long)priv->rx.virt, 4096); | ||
| 255 | free_tx: | ||
| 256 | gen_pool_free(priv->tx.pool, (unsigned long)priv->tx.virt, 4096); | ||
| 257 | |||
| 258 | return err; | ||
| 259 | } | ||
| 260 | |||
| 261 | static void tegra186_bpmp_deinit(struct tegra_bpmp *bpmp) | ||
| 262 | { | ||
| 263 | struct tegra186_bpmp *priv = bpmp->priv; | ||
| 264 | unsigned int i; | ||
| 265 | |||
| 266 | mbox_free_channel(priv->mbox.channel); | ||
| 267 | |||
| 268 | for (i = 0; i < bpmp->threaded.count; i++) | ||
| 269 | tegra186_bpmp_channel_cleanup(&bpmp->threaded_channels[i]); | ||
| 270 | |||
| 271 | tegra186_bpmp_channel_cleanup(bpmp->rx_channel); | ||
| 272 | tegra186_bpmp_channel_cleanup(bpmp->tx_channel); | ||
| 273 | |||
| 274 | gen_pool_free(priv->rx.pool, (unsigned long)priv->rx.virt, 4096); | ||
| 275 | gen_pool_free(priv->tx.pool, (unsigned long)priv->tx.virt, 4096); | ||
| 276 | } | ||
| 277 | |||
| 278 | static int tegra186_bpmp_resume(struct tegra_bpmp *bpmp) | ||
| 279 | { | ||
| 280 | unsigned int i; | ||
| 281 | |||
| 282 | /* reset message channels */ | ||
| 283 | tegra186_bpmp_channel_reset(bpmp->tx_channel); | ||
| 284 | tegra186_bpmp_channel_reset(bpmp->rx_channel); | ||
| 285 | |||
| 286 | for (i = 0; i < bpmp->threaded.count; i++) | ||
| 287 | tegra186_bpmp_channel_reset(&bpmp->threaded_channels[i]); | ||
| 288 | |||
| 289 | return 0; | ||
| 290 | } | ||
| 291 | |||
| 292 | const struct tegra_bpmp_ops tegra186_bpmp_ops = { | ||
| 293 | .init = tegra186_bpmp_init, | ||
| 294 | .deinit = tegra186_bpmp_deinit, | ||
| 295 | .is_response_ready = tegra186_bpmp_is_message_ready, | ||
| 296 | .is_request_ready = tegra186_bpmp_is_message_ready, | ||
| 297 | .ack_response = tegra186_bpmp_ack_message, | ||
| 298 | .ack_request = tegra186_bpmp_ack_message, | ||
| 299 | .is_response_channel_free = tegra186_bpmp_is_channel_free, | ||
| 300 | .is_request_channel_free = tegra186_bpmp_is_channel_free, | ||
| 301 | .post_response = tegra186_bpmp_post_message, | ||
| 302 | .post_request = tegra186_bpmp_post_message, | ||
| 303 | .ring_doorbell = tegra186_bpmp_ring_doorbell, | ||
| 304 | .resume = tegra186_bpmp_resume, | ||
| 305 | }; | ||
diff --git a/drivers/firmware/tegra/bpmp-tegra210.c b/drivers/firmware/tegra/bpmp-tegra210.c new file mode 100644 index 000000000000..ae15940a078e --- /dev/null +++ b/drivers/firmware/tegra/bpmp-tegra210.c | |||
| @@ -0,0 +1,243 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* | ||
| 3 | * Copyright (c) 2018, NVIDIA CORPORATION. | ||
| 4 | */ | ||
| 5 | |||
| 6 | #include <linux/interrupt.h> | ||
| 7 | #include <linux/irq.h> | ||
| 8 | #include <linux/io.h> | ||
| 9 | #include <linux/of.h> | ||
| 10 | #include <linux/platform_device.h> | ||
| 11 | |||
| 12 | #include <soc/tegra/bpmp.h> | ||
| 13 | |||
| 14 | #include "bpmp-private.h" | ||
| 15 | |||
| 16 | #define TRIGGER_OFFSET 0x000 | ||
| 17 | #define RESULT_OFFSET(id) (0xc00 + id * 4) | ||
| 18 | #define TRIGGER_ID_SHIFT 16 | ||
| 19 | #define TRIGGER_CMD_GET 4 | ||
| 20 | |||
| 21 | #define STA_OFFSET 0 | ||
| 22 | #define SET_OFFSET 4 | ||
| 23 | #define CLR_OFFSET 8 | ||
| 24 | |||
| 25 | #define CH_MASK(ch) (0x3 << ((ch) * 2)) | ||
| 26 | #define SL_SIGL(ch) (0x0 << ((ch) * 2)) | ||
| 27 | #define SL_QUED(ch) (0x1 << ((ch) * 2)) | ||
| 28 | #define MA_FREE(ch) (0x2 << ((ch) * 2)) | ||
| 29 | #define MA_ACKD(ch) (0x3 << ((ch) * 2)) | ||
| 30 | |||
| 31 | struct tegra210_bpmp { | ||
| 32 | void __iomem *atomics; | ||
| 33 | void __iomem *arb_sema; | ||
| 34 | struct irq_data *tx_irq_data; | ||
| 35 | }; | ||
| 36 | |||
| 37 | static u32 bpmp_channel_status(struct tegra_bpmp *bpmp, unsigned int index) | ||
| 38 | { | ||
| 39 | struct tegra210_bpmp *priv = bpmp->priv; | ||
| 40 | |||
| 41 | return __raw_readl(priv->arb_sema + STA_OFFSET) & CH_MASK(index); | ||
| 42 | } | ||
| 43 | |||
| 44 | static bool tegra210_bpmp_is_response_ready(struct tegra_bpmp_channel *channel) | ||
| 45 | { | ||
| 46 | unsigned int index = channel->index; | ||
| 47 | |||
| 48 | return bpmp_channel_status(channel->bpmp, index) == MA_ACKD(index); | ||
| 49 | } | ||
| 50 | |||
| 51 | static bool tegra210_bpmp_is_request_ready(struct tegra_bpmp_channel *channel) | ||
| 52 | { | ||
| 53 | unsigned int index = channel->index; | ||
| 54 | |||
| 55 | return bpmp_channel_status(channel->bpmp, index) == SL_SIGL(index); | ||
| 56 | } | ||
| 57 | |||
| 58 | static bool | ||
| 59 | tegra210_bpmp_is_request_channel_free(struct tegra_bpmp_channel *channel) | ||
| 60 | { | ||
| 61 | unsigned int index = channel->index; | ||
| 62 | |||
| 63 | return bpmp_channel_status(channel->bpmp, index) == MA_FREE(index); | ||
| 64 | } | ||
| 65 | |||
| 66 | static bool | ||
| 67 | tegra210_bpmp_is_response_channel_free(struct tegra_bpmp_channel *channel) | ||
| 68 | { | ||
| 69 | unsigned int index = channel->index; | ||
| 70 | |||
| 71 | return bpmp_channel_status(channel->bpmp, index) == SL_QUED(index); | ||
| 72 | } | ||
| 73 | |||
| 74 | static int tegra210_bpmp_post_request(struct tegra_bpmp_channel *channel) | ||
| 75 | { | ||
| 76 | struct tegra210_bpmp *priv = channel->bpmp->priv; | ||
| 77 | |||
| 78 | __raw_writel(CH_MASK(channel->index), priv->arb_sema + CLR_OFFSET); | ||
| 79 | |||
| 80 | return 0; | ||
| 81 | } | ||
| 82 | |||
| 83 | static int tegra210_bpmp_post_response(struct tegra_bpmp_channel *channel) | ||
| 84 | { | ||
| 85 | struct tegra210_bpmp *priv = channel->bpmp->priv; | ||
| 86 | |||
| 87 | __raw_writel(MA_ACKD(channel->index), priv->arb_sema + SET_OFFSET); | ||
| 88 | |||
| 89 | return 0; | ||
| 90 | } | ||
| 91 | |||
| 92 | static int tegra210_bpmp_ack_response(struct tegra_bpmp_channel *channel) | ||
| 93 | { | ||
| 94 | struct tegra210_bpmp *priv = channel->bpmp->priv; | ||
| 95 | |||
| 96 | __raw_writel(MA_ACKD(channel->index) ^ MA_FREE(channel->index), | ||
| 97 | priv->arb_sema + CLR_OFFSET); | ||
| 98 | |||
| 99 | return 0; | ||
| 100 | } | ||
| 101 | |||
| 102 | static int tegra210_bpmp_ack_request(struct tegra_bpmp_channel *channel) | ||
| 103 | { | ||
| 104 | struct tegra210_bpmp *priv = channel->bpmp->priv; | ||
| 105 | |||
| 106 | __raw_writel(SL_QUED(channel->index), priv->arb_sema + SET_OFFSET); | ||
| 107 | |||
| 108 | return 0; | ||
| 109 | } | ||
| 110 | |||
| 111 | static int tegra210_bpmp_ring_doorbell(struct tegra_bpmp *bpmp) | ||
| 112 | { | ||
| 113 | struct tegra210_bpmp *priv = bpmp->priv; | ||
| 114 | struct irq_data *irq_data = priv->tx_irq_data; | ||
| 115 | |||
| 116 | /* | ||
| 117 | * Tegra Legacy Interrupt Controller (LIC) is used to notify BPMP of | ||
| 118 | * available messages | ||
| 119 | */ | ||
| 120 | if (irq_data->chip->irq_retrigger) | ||
| 121 | return irq_data->chip->irq_retrigger(irq_data); | ||
| 122 | |||
| 123 | return -EINVAL; | ||
| 124 | } | ||
| 125 | |||
| 126 | static irqreturn_t rx_irq(int irq, void *data) | ||
| 127 | { | ||
| 128 | struct tegra_bpmp *bpmp = data; | ||
| 129 | |||
| 130 | tegra_bpmp_handle_rx(bpmp); | ||
| 131 | |||
| 132 | return IRQ_HANDLED; | ||
| 133 | } | ||
| 134 | |||
| 135 | static int tegra210_bpmp_channel_init(struct tegra_bpmp_channel *channel, | ||
| 136 | struct tegra_bpmp *bpmp, | ||
| 137 | unsigned int index) | ||
| 138 | { | ||
| 139 | struct tegra210_bpmp *priv = bpmp->priv; | ||
| 140 | u32 address; | ||
| 141 | void *p; | ||
| 142 | |||
| 143 | /* Retrieve channel base address from BPMP */ | ||
| 144 | writel(index << TRIGGER_ID_SHIFT | TRIGGER_CMD_GET, | ||
| 145 | priv->atomics + TRIGGER_OFFSET); | ||
| 146 | address = readl(priv->atomics + RESULT_OFFSET(index)); | ||
| 147 | |||
| 148 | p = devm_ioremap(bpmp->dev, address, 0x80); | ||
| 149 | if (!p) | ||
| 150 | return -ENOMEM; | ||
| 151 | |||
| 152 | channel->ib = p; | ||
| 153 | channel->ob = p; | ||
| 154 | channel->index = index; | ||
| 155 | init_completion(&channel->completion); | ||
| 156 | channel->bpmp = bpmp; | ||
| 157 | |||
| 158 | return 0; | ||
| 159 | } | ||
| 160 | |||
| 161 | static int tegra210_bpmp_init(struct tegra_bpmp *bpmp) | ||
| 162 | { | ||
| 163 | struct platform_device *pdev = to_platform_device(bpmp->dev); | ||
| 164 | struct tegra210_bpmp *priv; | ||
| 165 | struct resource *res; | ||
| 166 | unsigned int i; | ||
| 167 | int err; | ||
| 168 | |||
| 169 | priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); | ||
| 170 | if (!priv) | ||
| 171 | return -ENOMEM; | ||
| 172 | |||
| 173 | bpmp->priv = priv; | ||
| 174 | |||
| 175 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 176 | priv->atomics = devm_ioremap_resource(&pdev->dev, res); | ||
| 177 | if (IS_ERR(priv->atomics)) | ||
| 178 | return PTR_ERR(priv->atomics); | ||
| 179 | |||
| 180 | res = platform_get_resource(pdev, IORESOURCE_MEM, 1); | ||
| 181 | priv->arb_sema = devm_ioremap_resource(&pdev->dev, res); | ||
| 182 | if (IS_ERR(priv->arb_sema)) | ||
| 183 | return PTR_ERR(priv->arb_sema); | ||
| 184 | |||
| 185 | err = tegra210_bpmp_channel_init(bpmp->tx_channel, bpmp, | ||
| 186 | bpmp->soc->channels.cpu_tx.offset); | ||
| 187 | if (err < 0) | ||
| 188 | return err; | ||
| 189 | |||
| 190 | err = tegra210_bpmp_channel_init(bpmp->rx_channel, bpmp, | ||
| 191 | bpmp->soc->channels.cpu_rx.offset); | ||
| 192 | if (err < 0) | ||
| 193 | return err; | ||
| 194 | |||
| 195 | for (i = 0; i < bpmp->threaded.count; i++) { | ||
| 196 | unsigned int index = bpmp->soc->channels.thread.offset + i; | ||
| 197 | |||
| 198 | err = tegra210_bpmp_channel_init(&bpmp->threaded_channels[i], | ||
| 199 | bpmp, index); | ||
| 200 | if (err < 0) | ||
| 201 | return err; | ||
| 202 | } | ||
| 203 | |||
| 204 | err = platform_get_irq_byname(pdev, "tx"); | ||
| 205 | if (err < 0) { | ||
| 206 | dev_err(&pdev->dev, "failed to get TX IRQ: %d\n", err); | ||
| 207 | return err; | ||
| 208 | } | ||
| 209 | |||
| 210 | priv->tx_irq_data = irq_get_irq_data(err); | ||
| 211 | if (!priv->tx_irq_data) { | ||
| 212 | dev_err(&pdev->dev, "failed to get IRQ data for TX IRQ\n"); | ||
| 213 | return err; | ||
| 214 | } | ||
| 215 | |||
| 216 | err = platform_get_irq_byname(pdev, "rx"); | ||
| 217 | if (err < 0) { | ||
| 218 | dev_err(&pdev->dev, "failed to get rx IRQ: %d\n", err); | ||
| 219 | return err; | ||
| 220 | } | ||
| 221 | |||
| 222 | err = devm_request_irq(&pdev->dev, err, rx_irq, | ||
| 223 | IRQF_NO_SUSPEND, dev_name(&pdev->dev), bpmp); | ||
| 224 | if (err < 0) { | ||
| 225 | dev_err(&pdev->dev, "failed to request IRQ: %d\n", err); | ||
| 226 | return err; | ||
| 227 | } | ||
| 228 | |||
| 229 | return 0; | ||
| 230 | } | ||
| 231 | |||
| 232 | const struct tegra_bpmp_ops tegra210_bpmp_ops = { | ||
| 233 | .init = tegra210_bpmp_init, | ||
| 234 | .is_response_ready = tegra210_bpmp_is_response_ready, | ||
| 235 | .is_request_ready = tegra210_bpmp_is_request_ready, | ||
| 236 | .ack_response = tegra210_bpmp_ack_response, | ||
| 237 | .ack_request = tegra210_bpmp_ack_request, | ||
| 238 | .is_response_channel_free = tegra210_bpmp_is_response_channel_free, | ||
| 239 | .is_request_channel_free = tegra210_bpmp_is_request_channel_free, | ||
| 240 | .post_response = tegra210_bpmp_post_response, | ||
| 241 | .post_request = tegra210_bpmp_post_request, | ||
| 242 | .ring_doorbell = tegra210_bpmp_ring_doorbell, | ||
| 243 | }; | ||
diff --git a/drivers/firmware/tegra/bpmp.c b/drivers/firmware/tegra/bpmp.c index 689478b92bce..dd775e8ba5a0 100644 --- a/drivers/firmware/tegra/bpmp.c +++ b/drivers/firmware/tegra/bpmp.c | |||
| @@ -26,6 +26,8 @@ | |||
| 26 | #include <soc/tegra/bpmp-abi.h> | 26 | #include <soc/tegra/bpmp-abi.h> |
| 27 | #include <soc/tegra/ivc.h> | 27 | #include <soc/tegra/ivc.h> |
| 28 | 28 | ||
| 29 | #include "bpmp-private.h" | ||
| 30 | |||
| 29 | #define MSG_ACK BIT(0) | 31 | #define MSG_ACK BIT(0) |
| 30 | #define MSG_RING BIT(1) | 32 | #define MSG_RING BIT(1) |
| 31 | #define TAG_SZ 32 | 33 | #define TAG_SZ 32 |
| @@ -36,6 +38,14 @@ mbox_client_to_bpmp(struct mbox_client *client) | |||
| 36 | return container_of(client, struct tegra_bpmp, mbox.client); | 38 | return container_of(client, struct tegra_bpmp, mbox.client); |
| 37 | } | 39 | } |
| 38 | 40 | ||
| 41 | static inline const struct tegra_bpmp_ops * | ||
| 42 | channel_to_ops(struct tegra_bpmp_channel *channel) | ||
| 43 | { | ||
| 44 | struct tegra_bpmp *bpmp = channel->bpmp; | ||
| 45 | |||
| 46 | return bpmp->soc->ops; | ||
| 47 | } | ||
| 48 | |||
| 39 | struct tegra_bpmp *tegra_bpmp_get(struct device *dev) | 49 | struct tegra_bpmp *tegra_bpmp_get(struct device *dev) |
| 40 | { | 50 | { |
| 41 | struct platform_device *pdev; | 51 | struct platform_device *pdev; |
| @@ -96,22 +106,21 @@ static bool tegra_bpmp_message_valid(const struct tegra_bpmp_message *msg) | |||
| 96 | (msg->rx.size == 0 || msg->rx.data); | 106 | (msg->rx.size == 0 || msg->rx.data); |
| 97 | } | 107 | } |
| 98 | 108 | ||
| 99 | static bool tegra_bpmp_master_acked(struct tegra_bpmp_channel *channel) | 109 | static bool tegra_bpmp_is_response_ready(struct tegra_bpmp_channel *channel) |
| 100 | { | 110 | { |
| 101 | void *frame; | 111 | const struct tegra_bpmp_ops *ops = channel_to_ops(channel); |
| 102 | 112 | ||
| 103 | frame = tegra_ivc_read_get_next_frame(channel->ivc); | 113 | return ops->is_response_ready(channel); |
| 104 | if (IS_ERR(frame)) { | 114 | } |
| 105 | channel->ib = NULL; | ||
| 106 | return false; | ||
| 107 | } | ||
| 108 | 115 | ||
| 109 | channel->ib = frame; | 116 | static bool tegra_bpmp_is_request_ready(struct tegra_bpmp_channel *channel) |
| 117 | { | ||
| 118 | const struct tegra_bpmp_ops *ops = channel_to_ops(channel); | ||
| 110 | 119 | ||
| 111 | return true; | 120 | return ops->is_request_ready(channel); |
| 112 | } | 121 | } |
| 113 | 122 | ||
| 114 | static int tegra_bpmp_wait_ack(struct tegra_bpmp_channel *channel) | 123 | static int tegra_bpmp_wait_response(struct tegra_bpmp_channel *channel) |
| 115 | { | 124 | { |
| 116 | unsigned long timeout = channel->bpmp->soc->channels.cpu_tx.timeout; | 125 | unsigned long timeout = channel->bpmp->soc->channels.cpu_tx.timeout; |
| 117 | ktime_t end; | 126 | ktime_t end; |
| @@ -119,29 +128,45 @@ static int tegra_bpmp_wait_ack(struct tegra_bpmp_channel *channel) | |||
| 119 | end = ktime_add_us(ktime_get(), timeout); | 128 | end = ktime_add_us(ktime_get(), timeout); |
| 120 | 129 | ||
| 121 | do { | 130 | do { |
| 122 | if (tegra_bpmp_master_acked(channel)) | 131 | if (tegra_bpmp_is_response_ready(channel)) |
| 123 | return 0; | 132 | return 0; |
| 124 | } while (ktime_before(ktime_get(), end)); | 133 | } while (ktime_before(ktime_get(), end)); |
| 125 | 134 | ||
| 126 | return -ETIMEDOUT; | 135 | return -ETIMEDOUT; |
| 127 | } | 136 | } |
| 128 | 137 | ||
| 129 | static bool tegra_bpmp_master_free(struct tegra_bpmp_channel *channel) | 138 | static int tegra_bpmp_ack_response(struct tegra_bpmp_channel *channel) |
| 130 | { | 139 | { |
| 131 | void *frame; | 140 | const struct tegra_bpmp_ops *ops = channel_to_ops(channel); |
| 132 | 141 | ||
| 133 | frame = tegra_ivc_write_get_next_frame(channel->ivc); | 142 | return ops->ack_response(channel); |
| 134 | if (IS_ERR(frame)) { | 143 | } |
| 135 | channel->ob = NULL; | ||
| 136 | return false; | ||
| 137 | } | ||
| 138 | 144 | ||
| 139 | channel->ob = frame; | 145 | static int tegra_bpmp_ack_request(struct tegra_bpmp_channel *channel) |
| 146 | { | ||
| 147 | const struct tegra_bpmp_ops *ops = channel_to_ops(channel); | ||
| 140 | 148 | ||
| 141 | return true; | 149 | return ops->ack_request(channel); |
| 142 | } | 150 | } |
| 143 | 151 | ||
| 144 | static int tegra_bpmp_wait_master_free(struct tegra_bpmp_channel *channel) | 152 | static bool |
| 153 | tegra_bpmp_is_request_channel_free(struct tegra_bpmp_channel *channel) | ||
| 154 | { | ||
| 155 | const struct tegra_bpmp_ops *ops = channel_to_ops(channel); | ||
| 156 | |||
| 157 | return ops->is_request_channel_free(channel); | ||
| 158 | } | ||
| 159 | |||
| 160 | static bool | ||
| 161 | tegra_bpmp_is_response_channel_free(struct tegra_bpmp_channel *channel) | ||
| 162 | { | ||
| 163 | const struct tegra_bpmp_ops *ops = channel_to_ops(channel); | ||
| 164 | |||
| 165 | return ops->is_response_channel_free(channel); | ||
| 166 | } | ||
| 167 | |||
| 168 | static int | ||
| 169 | tegra_bpmp_wait_request_channel_free(struct tegra_bpmp_channel *channel) | ||
| 145 | { | 170 | { |
| 146 | unsigned long timeout = channel->bpmp->soc->channels.cpu_tx.timeout; | 171 | unsigned long timeout = channel->bpmp->soc->channels.cpu_tx.timeout; |
| 147 | ktime_t start, now; | 172 | ktime_t start, now; |
| @@ -149,7 +174,7 @@ static int tegra_bpmp_wait_master_free(struct tegra_bpmp_channel *channel) | |||
| 149 | start = ns_to_ktime(local_clock()); | 174 | start = ns_to_ktime(local_clock()); |
| 150 | 175 | ||
| 151 | do { | 176 | do { |
| 152 | if (tegra_bpmp_master_free(channel)) | 177 | if (tegra_bpmp_is_request_channel_free(channel)) |
| 153 | return 0; | 178 | return 0; |
| 154 | 179 | ||
| 155 | now = ns_to_ktime(local_clock()); | 180 | now = ns_to_ktime(local_clock()); |
| @@ -158,6 +183,25 @@ static int tegra_bpmp_wait_master_free(struct tegra_bpmp_channel *channel) | |||
| 158 | return -ETIMEDOUT; | 183 | return -ETIMEDOUT; |
| 159 | } | 184 | } |
| 160 | 185 | ||
| 186 | static int tegra_bpmp_post_request(struct tegra_bpmp_channel *channel) | ||
| 187 | { | ||
| 188 | const struct tegra_bpmp_ops *ops = channel_to_ops(channel); | ||
| 189 | |||
| 190 | return ops->post_request(channel); | ||
| 191 | } | ||
| 192 | |||
| 193 | static int tegra_bpmp_post_response(struct tegra_bpmp_channel *channel) | ||
| 194 | { | ||
| 195 | const struct tegra_bpmp_ops *ops = channel_to_ops(channel); | ||
| 196 | |||
| 197 | return ops->post_response(channel); | ||
| 198 | } | ||
| 199 | |||
| 200 | static int tegra_bpmp_ring_doorbell(struct tegra_bpmp *bpmp) | ||
| 201 | { | ||
| 202 | return bpmp->soc->ops->ring_doorbell(bpmp); | ||
| 203 | } | ||
| 204 | |||
| 161 | static ssize_t __tegra_bpmp_channel_read(struct tegra_bpmp_channel *channel, | 205 | static ssize_t __tegra_bpmp_channel_read(struct tegra_bpmp_channel *channel, |
| 162 | void *data, size_t size, int *ret) | 206 | void *data, size_t size, int *ret) |
| 163 | { | 207 | { |
| @@ -166,7 +210,7 @@ static ssize_t __tegra_bpmp_channel_read(struct tegra_bpmp_channel *channel, | |||
| 166 | if (data && size > 0) | 210 | if (data && size > 0) |
| 167 | memcpy(data, channel->ib->data, size); | 211 | memcpy(data, channel->ib->data, size); |
| 168 | 212 | ||
| 169 | err = tegra_ivc_read_advance(channel->ivc); | 213 | err = tegra_bpmp_ack_response(channel); |
| 170 | if (err < 0) | 214 | if (err < 0) |
| 171 | return err; | 215 | return err; |
| 172 | 216 | ||
| @@ -210,7 +254,7 @@ static ssize_t __tegra_bpmp_channel_write(struct tegra_bpmp_channel *channel, | |||
| 210 | if (data && size > 0) | 254 | if (data && size > 0) |
| 211 | memcpy(channel->ob->data, data, size); | 255 | memcpy(channel->ob->data, data, size); |
| 212 | 256 | ||
| 213 | return tegra_ivc_write_advance(channel->ivc); | 257 | return tegra_bpmp_post_request(channel); |
| 214 | } | 258 | } |
| 215 | 259 | ||
| 216 | static struct tegra_bpmp_channel * | 260 | static struct tegra_bpmp_channel * |
| @@ -238,7 +282,7 @@ tegra_bpmp_write_threaded(struct tegra_bpmp *bpmp, unsigned int mrq, | |||
| 238 | 282 | ||
| 239 | channel = &bpmp->threaded_channels[index]; | 283 | channel = &bpmp->threaded_channels[index]; |
| 240 | 284 | ||
| 241 | if (!tegra_bpmp_master_free(channel)) { | 285 | if (!tegra_bpmp_is_request_channel_free(channel)) { |
| 242 | err = -EBUSY; | 286 | err = -EBUSY; |
| 243 | goto unlock; | 287 | goto unlock; |
| 244 | } | 288 | } |
| @@ -270,7 +314,7 @@ static ssize_t tegra_bpmp_channel_write(struct tegra_bpmp_channel *channel, | |||
| 270 | { | 314 | { |
| 271 | int err; | 315 | int err; |
| 272 | 316 | ||
| 273 | err = tegra_bpmp_wait_master_free(channel); | 317 | err = tegra_bpmp_wait_request_channel_free(channel); |
| 274 | if (err < 0) | 318 | if (err < 0) |
| 275 | return err; | 319 | return err; |
| 276 | 320 | ||
| @@ -302,13 +346,11 @@ int tegra_bpmp_transfer_atomic(struct tegra_bpmp *bpmp, | |||
| 302 | 346 | ||
| 303 | spin_unlock(&bpmp->atomic_tx_lock); | 347 | spin_unlock(&bpmp->atomic_tx_lock); |
| 304 | 348 | ||
| 305 | err = mbox_send_message(bpmp->mbox.channel, NULL); | 349 | err = tegra_bpmp_ring_doorbell(bpmp); |
| 306 | if (err < 0) | 350 | if (err < 0) |
| 307 | return err; | 351 | return err; |
| 308 | 352 | ||
| 309 | mbox_client_txdone(bpmp->mbox.channel, 0); | 353 | err = tegra_bpmp_wait_response(channel); |
| 310 | |||
| 311 | err = tegra_bpmp_wait_ack(channel); | ||
| 312 | if (err < 0) | 354 | if (err < 0) |
| 313 | return err; | 355 | return err; |
| 314 | 356 | ||
| @@ -335,12 +377,10 @@ int tegra_bpmp_transfer(struct tegra_bpmp *bpmp, | |||
| 335 | if (IS_ERR(channel)) | 377 | if (IS_ERR(channel)) |
| 336 | return PTR_ERR(channel); | 378 | return PTR_ERR(channel); |
| 337 | 379 | ||
| 338 | err = mbox_send_message(bpmp->mbox.channel, NULL); | 380 | err = tegra_bpmp_ring_doorbell(bpmp); |
| 339 | if (err < 0) | 381 | if (err < 0) |
| 340 | return err; | 382 | return err; |
| 341 | 383 | ||
| 342 | mbox_client_txdone(bpmp->mbox.channel, 0); | ||
| 343 | |||
| 344 | timeout = usecs_to_jiffies(bpmp->soc->channels.thread.timeout); | 384 | timeout = usecs_to_jiffies(bpmp->soc->channels.thread.timeout); |
| 345 | 385 | ||
| 346 | err = wait_for_completion_timeout(&channel->completion, timeout); | 386 | err = wait_for_completion_timeout(&channel->completion, timeout); |
| @@ -369,38 +409,34 @@ void tegra_bpmp_mrq_return(struct tegra_bpmp_channel *channel, int code, | |||
| 369 | { | 409 | { |
| 370 | unsigned long flags = channel->ib->flags; | 410 | unsigned long flags = channel->ib->flags; |
| 371 | struct tegra_bpmp *bpmp = channel->bpmp; | 411 | struct tegra_bpmp *bpmp = channel->bpmp; |
| 372 | struct tegra_bpmp_mb_data *frame; | ||
| 373 | int err; | 412 | int err; |
| 374 | 413 | ||
| 375 | if (WARN_ON(size > MSG_DATA_MIN_SZ)) | 414 | if (WARN_ON(size > MSG_DATA_MIN_SZ)) |
| 376 | return; | 415 | return; |
| 377 | 416 | ||
| 378 | err = tegra_ivc_read_advance(channel->ivc); | 417 | err = tegra_bpmp_ack_request(channel); |
| 379 | if (WARN_ON(err < 0)) | 418 | if (WARN_ON(err < 0)) |
| 380 | return; | 419 | return; |
| 381 | 420 | ||
| 382 | if ((flags & MSG_ACK) == 0) | 421 | if ((flags & MSG_ACK) == 0) |
| 383 | return; | 422 | return; |
| 384 | 423 | ||
| 385 | frame = tegra_ivc_write_get_next_frame(channel->ivc); | 424 | if (WARN_ON(!tegra_bpmp_is_response_channel_free(channel))) |
| 386 | if (WARN_ON(IS_ERR(frame))) | ||
| 387 | return; | 425 | return; |
| 388 | 426 | ||
| 389 | frame->code = code; | 427 | channel->ob->code = code; |
| 390 | 428 | ||
| 391 | if (data && size > 0) | 429 | if (data && size > 0) |
| 392 | memcpy(frame->data, data, size); | 430 | memcpy(channel->ob->data, data, size); |
| 393 | 431 | ||
| 394 | err = tegra_ivc_write_advance(channel->ivc); | 432 | err = tegra_bpmp_post_response(channel); |
| 395 | if (WARN_ON(err < 0)) | 433 | if (WARN_ON(err < 0)) |
| 396 | return; | 434 | return; |
| 397 | 435 | ||
| 398 | if (flags & MSG_RING) { | 436 | if (flags & MSG_RING) { |
| 399 | err = mbox_send_message(bpmp->mbox.channel, NULL); | 437 | err = tegra_bpmp_ring_doorbell(bpmp); |
| 400 | if (WARN_ON(err < 0)) | 438 | if (WARN_ON(err < 0)) |
| 401 | return; | 439 | return; |
| 402 | |||
| 403 | mbox_client_txdone(bpmp->mbox.channel, 0); | ||
| 404 | } | 440 | } |
| 405 | } | 441 | } |
| 406 | EXPORT_SYMBOL_GPL(tegra_bpmp_mrq_return); | 442 | EXPORT_SYMBOL_GPL(tegra_bpmp_mrq_return); |
| @@ -627,9 +663,8 @@ static void tegra_bpmp_channel_signal(struct tegra_bpmp_channel *channel) | |||
| 627 | complete(&channel->completion); | 663 | complete(&channel->completion); |
| 628 | } | 664 | } |
| 629 | 665 | ||
| 630 | static void tegra_bpmp_handle_rx(struct mbox_client *client, void *data) | 666 | void tegra_bpmp_handle_rx(struct tegra_bpmp *bpmp) |
| 631 | { | 667 | { |
| 632 | struct tegra_bpmp *bpmp = mbox_client_to_bpmp(client); | ||
| 633 | struct tegra_bpmp_channel *channel; | 668 | struct tegra_bpmp_channel *channel; |
| 634 | unsigned int i, count; | 669 | unsigned int i, count; |
| 635 | unsigned long *busy; | 670 | unsigned long *busy; |
| @@ -638,7 +673,7 @@ static void tegra_bpmp_handle_rx(struct mbox_client *client, void *data) | |||
| 638 | count = bpmp->soc->channels.thread.count; | 673 | count = bpmp->soc->channels.thread.count; |
| 639 | busy = bpmp->threaded.busy; | 674 | busy = bpmp->threaded.busy; |
| 640 | 675 | ||
| 641 | if (tegra_bpmp_master_acked(channel)) | 676 | if (tegra_bpmp_is_request_ready(channel)) |
| 642 | tegra_bpmp_handle_mrq(bpmp, channel->ib->code, channel); | 677 | tegra_bpmp_handle_mrq(bpmp, channel->ib->code, channel); |
| 643 | 678 | ||
| 644 | spin_lock(&bpmp->lock); | 679 | spin_lock(&bpmp->lock); |
| @@ -648,7 +683,7 @@ static void tegra_bpmp_handle_rx(struct mbox_client *client, void *data) | |||
| 648 | 683 | ||
| 649 | channel = &bpmp->threaded_channels[i]; | 684 | channel = &bpmp->threaded_channels[i]; |
| 650 | 685 | ||
| 651 | if (tegra_bpmp_master_acked(channel)) { | 686 | if (tegra_bpmp_is_response_ready(channel)) { |
| 652 | tegra_bpmp_channel_signal(channel); | 687 | tegra_bpmp_channel_signal(channel); |
| 653 | clear_bit(i, busy); | 688 | clear_bit(i, busy); |
| 654 | } | 689 | } |
| @@ -657,74 +692,9 @@ static void tegra_bpmp_handle_rx(struct mbox_client *client, void *data) | |||
| 657 | spin_unlock(&bpmp->lock); | 692 | spin_unlock(&bpmp->lock); |
| 658 | } | 693 | } |
| 659 | 694 | ||
| 660 | static void tegra_bpmp_ivc_notify(struct tegra_ivc *ivc, void *data) | ||
| 661 | { | ||
| 662 | struct tegra_bpmp *bpmp = data; | ||
| 663 | int err; | ||
| 664 | |||
| 665 | if (WARN_ON(bpmp->mbox.channel == NULL)) | ||
| 666 | return; | ||
| 667 | |||
| 668 | err = mbox_send_message(bpmp->mbox.channel, NULL); | ||
| 669 | if (err < 0) | ||
| 670 | return; | ||
| 671 | |||
| 672 | mbox_client_txdone(bpmp->mbox.channel, 0); | ||
| 673 | } | ||
| 674 | |||
| 675 | static int tegra_bpmp_channel_init(struct tegra_bpmp_channel *channel, | ||
| 676 | struct tegra_bpmp *bpmp, | ||
| 677 | unsigned int index) | ||
| 678 | { | ||
| 679 | size_t message_size, queue_size; | ||
| 680 | unsigned int offset; | ||
| 681 | int err; | ||
| 682 | |||
| 683 | channel->ivc = devm_kzalloc(bpmp->dev, sizeof(*channel->ivc), | ||
| 684 | GFP_KERNEL); | ||
| 685 | if (!channel->ivc) | ||
| 686 | return -ENOMEM; | ||
| 687 | |||
| 688 | message_size = tegra_ivc_align(MSG_MIN_SZ); | ||
| 689 | queue_size = tegra_ivc_total_queue_size(message_size); | ||
| 690 | offset = queue_size * index; | ||
| 691 | |||
| 692 | err = tegra_ivc_init(channel->ivc, NULL, | ||
| 693 | bpmp->rx.virt + offset, bpmp->rx.phys + offset, | ||
| 694 | bpmp->tx.virt + offset, bpmp->tx.phys + offset, | ||
| 695 | 1, message_size, tegra_bpmp_ivc_notify, | ||
| 696 | bpmp); | ||
| 697 | if (err < 0) { | ||
| 698 | dev_err(bpmp->dev, "failed to setup IVC for channel %u: %d\n", | ||
| 699 | index, err); | ||
| 700 | return err; | ||
| 701 | } | ||
| 702 | |||
| 703 | init_completion(&channel->completion); | ||
| 704 | channel->bpmp = bpmp; | ||
| 705 | |||
| 706 | return 0; | ||
| 707 | } | ||
| 708 | |||
| 709 | static void tegra_bpmp_channel_reset(struct tegra_bpmp_channel *channel) | ||
| 710 | { | ||
| 711 | /* reset the channel state */ | ||
| 712 | tegra_ivc_reset(channel->ivc); | ||
| 713 | |||
| 714 | /* sync the channel state with BPMP */ | ||
| 715 | while (tegra_ivc_notified(channel->ivc)) | ||
| 716 | ; | ||
| 717 | } | ||
| 718 | |||
| 719 | static void tegra_bpmp_channel_cleanup(struct tegra_bpmp_channel *channel) | ||
| 720 | { | ||
| 721 | tegra_ivc_cleanup(channel->ivc); | ||
| 722 | } | ||
| 723 | |||
| 724 | static int tegra_bpmp_probe(struct platform_device *pdev) | 695 | static int tegra_bpmp_probe(struct platform_device *pdev) |
| 725 | { | 696 | { |
| 726 | struct tegra_bpmp *bpmp; | 697 | struct tegra_bpmp *bpmp; |
| 727 | unsigned int i; | ||
| 728 | char tag[TAG_SZ]; | 698 | char tag[TAG_SZ]; |
| 729 | size_t size; | 699 | size_t size; |
| 730 | int err; | 700 | int err; |
| @@ -736,32 +706,6 @@ static int tegra_bpmp_probe(struct platform_device *pdev) | |||
| 736 | bpmp->soc = of_device_get_match_data(&pdev->dev); | 706 | bpmp->soc = of_device_get_match_data(&pdev->dev); |
| 737 | bpmp->dev = &pdev->dev; | 707 | bpmp->dev = &pdev->dev; |
| 738 | 708 | ||
| 739 | bpmp->tx.pool = of_gen_pool_get(pdev->dev.of_node, "shmem", 0); | ||
| 740 | if (!bpmp->tx.pool) { | ||
| 741 | dev_err(&pdev->dev, "TX shmem pool not found\n"); | ||
| 742 | return -ENOMEM; | ||
| 743 | } | ||
| 744 | |||
| 745 | bpmp->tx.virt = gen_pool_dma_alloc(bpmp->tx.pool, 4096, &bpmp->tx.phys); | ||
| 746 | if (!bpmp->tx.virt) { | ||
| 747 | dev_err(&pdev->dev, "failed to allocate from TX pool\n"); | ||
| 748 | return -ENOMEM; | ||
| 749 | } | ||
| 750 | |||
| 751 | bpmp->rx.pool = of_gen_pool_get(pdev->dev.of_node, "shmem", 1); | ||
| 752 | if (!bpmp->rx.pool) { | ||
| 753 | dev_err(&pdev->dev, "RX shmem pool not found\n"); | ||
| 754 | err = -ENOMEM; | ||
| 755 | goto free_tx; | ||
| 756 | } | ||
| 757 | |||
| 758 | bpmp->rx.virt = gen_pool_dma_alloc(bpmp->rx.pool, 4096, &bpmp->rx.phys); | ||
| 759 | if (!bpmp->rx.virt) { | ||
| 760 | dev_err(&pdev->dev, "failed to allocate from RX pool\n"); | ||
| 761 | err = -ENOMEM; | ||
| 762 | goto free_tx; | ||
| 763 | } | ||
| 764 | |||
| 765 | INIT_LIST_HEAD(&bpmp->mrqs); | 709 | INIT_LIST_HEAD(&bpmp->mrqs); |
| 766 | spin_lock_init(&bpmp->lock); | 710 | spin_lock_init(&bpmp->lock); |
| 767 | 711 | ||
| @@ -771,81 +715,38 @@ static int tegra_bpmp_probe(struct platform_device *pdev) | |||
| 771 | size = BITS_TO_LONGS(bpmp->threaded.count) * sizeof(long); | 715 | size = BITS_TO_LONGS(bpmp->threaded.count) * sizeof(long); |
| 772 | 716 | ||
| 773 | bpmp->threaded.allocated = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); | 717 | bpmp->threaded.allocated = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); |
| 774 | if (!bpmp->threaded.allocated) { | 718 | if (!bpmp->threaded.allocated) |
| 775 | err = -ENOMEM; | 719 | return -ENOMEM; |
| 776 | goto free_rx; | ||
| 777 | } | ||
| 778 | 720 | ||
| 779 | bpmp->threaded.busy = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); | 721 | bpmp->threaded.busy = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); |
| 780 | if (!bpmp->threaded.busy) { | 722 | if (!bpmp->threaded.busy) |
| 781 | err = -ENOMEM; | 723 | return -ENOMEM; |
| 782 | goto free_rx; | ||
| 783 | } | ||
| 784 | 724 | ||
| 785 | spin_lock_init(&bpmp->atomic_tx_lock); | 725 | spin_lock_init(&bpmp->atomic_tx_lock); |
| 786 | bpmp->tx_channel = devm_kzalloc(&pdev->dev, sizeof(*bpmp->tx_channel), | 726 | bpmp->tx_channel = devm_kzalloc(&pdev->dev, sizeof(*bpmp->tx_channel), |
| 787 | GFP_KERNEL); | 727 | GFP_KERNEL); |
| 788 | if (!bpmp->tx_channel) { | 728 | if (!bpmp->tx_channel) |
| 789 | err = -ENOMEM; | 729 | return -ENOMEM; |
| 790 | goto free_rx; | ||
| 791 | } | ||
| 792 | 730 | ||
| 793 | bpmp->rx_channel = devm_kzalloc(&pdev->dev, sizeof(*bpmp->rx_channel), | 731 | bpmp->rx_channel = devm_kzalloc(&pdev->dev, sizeof(*bpmp->rx_channel), |
| 794 | GFP_KERNEL); | 732 | GFP_KERNEL); |
| 795 | if (!bpmp->rx_channel) { | 733 | if (!bpmp->rx_channel) |
| 796 | err = -ENOMEM; | 734 | return -ENOMEM; |
| 797 | goto free_rx; | ||
| 798 | } | ||
| 799 | 735 | ||
| 800 | bpmp->threaded_channels = devm_kcalloc(&pdev->dev, bpmp->threaded.count, | 736 | bpmp->threaded_channels = devm_kcalloc(&pdev->dev, bpmp->threaded.count, |
| 801 | sizeof(*bpmp->threaded_channels), | 737 | sizeof(*bpmp->threaded_channels), |
| 802 | GFP_KERNEL); | 738 | GFP_KERNEL); |
| 803 | if (!bpmp->threaded_channels) { | 739 | if (!bpmp->threaded_channels) |
| 804 | err = -ENOMEM; | 740 | return -ENOMEM; |
| 805 | goto free_rx; | ||
| 806 | } | ||
| 807 | |||
| 808 | err = tegra_bpmp_channel_init(bpmp->tx_channel, bpmp, | ||
| 809 | bpmp->soc->channels.cpu_tx.offset); | ||
| 810 | if (err < 0) | ||
| 811 | goto free_rx; | ||
| 812 | 741 | ||
| 813 | err = tegra_bpmp_channel_init(bpmp->rx_channel, bpmp, | 742 | err = bpmp->soc->ops->init(bpmp); |
| 814 | bpmp->soc->channels.cpu_rx.offset); | ||
| 815 | if (err < 0) | 743 | if (err < 0) |
| 816 | goto cleanup_tx_channel; | 744 | return err; |
| 817 | |||
| 818 | for (i = 0; i < bpmp->threaded.count; i++) { | ||
| 819 | err = tegra_bpmp_channel_init( | ||
| 820 | &bpmp->threaded_channels[i], bpmp, | ||
| 821 | bpmp->soc->channels.thread.offset + i); | ||
| 822 | if (err < 0) | ||
| 823 | goto cleanup_threaded_channels; | ||
| 824 | } | ||
| 825 | |||
| 826 | /* mbox registration */ | ||
| 827 | bpmp->mbox.client.dev = &pdev->dev; | ||
| 828 | bpmp->mbox.client.rx_callback = tegra_bpmp_handle_rx; | ||
| 829 | bpmp->mbox.client.tx_block = false; | ||
| 830 | bpmp->mbox.client.knows_txdone = false; | ||
| 831 | |||
| 832 | bpmp->mbox.channel = mbox_request_channel(&bpmp->mbox.client, 0); | ||
| 833 | if (IS_ERR(bpmp->mbox.channel)) { | ||
| 834 | err = PTR_ERR(bpmp->mbox.channel); | ||
| 835 | dev_err(&pdev->dev, "failed to get HSP mailbox: %d\n", err); | ||
| 836 | goto cleanup_threaded_channels; | ||
| 837 | } | ||
| 838 | |||
| 839 | /* reset message channels */ | ||
| 840 | tegra_bpmp_channel_reset(bpmp->tx_channel); | ||
| 841 | tegra_bpmp_channel_reset(bpmp->rx_channel); | ||
| 842 | for (i = 0; i < bpmp->threaded.count; i++) | ||
| 843 | tegra_bpmp_channel_reset(&bpmp->threaded_channels[i]); | ||
| 844 | 745 | ||
| 845 | err = tegra_bpmp_request_mrq(bpmp, MRQ_PING, | 746 | err = tegra_bpmp_request_mrq(bpmp, MRQ_PING, |
| 846 | tegra_bpmp_mrq_handle_ping, bpmp); | 747 | tegra_bpmp_mrq_handle_ping, bpmp); |
| 847 | if (err < 0) | 748 | if (err < 0) |
| 848 | goto free_mbox; | 749 | goto deinit; |
| 849 | 750 | ||
| 850 | err = tegra_bpmp_ping(bpmp); | 751 | err = tegra_bpmp_ping(bpmp); |
| 851 | if (err < 0) { | 752 | if (err < 0) { |
| @@ -867,17 +768,23 @@ static int tegra_bpmp_probe(struct platform_device *pdev) | |||
| 867 | if (err < 0) | 768 | if (err < 0) |
| 868 | goto free_mrq; | 769 | goto free_mrq; |
| 869 | 770 | ||
| 870 | err = tegra_bpmp_init_clocks(bpmp); | 771 | if (of_find_property(pdev->dev.of_node, "#clock-cells", NULL)) { |
| 871 | if (err < 0) | 772 | err = tegra_bpmp_init_clocks(bpmp); |
| 872 | goto free_mrq; | 773 | if (err < 0) |
| 774 | goto free_mrq; | ||
| 775 | } | ||
| 873 | 776 | ||
| 874 | err = tegra_bpmp_init_resets(bpmp); | 777 | if (of_find_property(pdev->dev.of_node, "#reset-cells", NULL)) { |
| 875 | if (err < 0) | 778 | err = tegra_bpmp_init_resets(bpmp); |
| 876 | goto free_mrq; | 779 | if (err < 0) |
| 780 | goto free_mrq; | ||
| 781 | } | ||
| 877 | 782 | ||
| 878 | err = tegra_bpmp_init_powergates(bpmp); | 783 | if (of_find_property(pdev->dev.of_node, "#power-domain-cells", NULL)) { |
| 879 | if (err < 0) | 784 | err = tegra_bpmp_init_powergates(bpmp); |
| 880 | goto free_mrq; | 785 | if (err < 0) |
| 786 | goto free_mrq; | ||
| 787 | } | ||
| 881 | 788 | ||
| 882 | err = tegra_bpmp_init_debugfs(bpmp); | 789 | err = tegra_bpmp_init_debugfs(bpmp); |
| 883 | if (err < 0) | 790 | if (err < 0) |
| @@ -887,41 +794,27 @@ static int tegra_bpmp_probe(struct platform_device *pdev) | |||
| 887 | 794 | ||
| 888 | free_mrq: | 795 | free_mrq: |
| 889 | tegra_bpmp_free_mrq(bpmp, MRQ_PING, bpmp); | 796 | tegra_bpmp_free_mrq(bpmp, MRQ_PING, bpmp); |
| 890 | free_mbox: | 797 | deinit: |
| 891 | mbox_free_channel(bpmp->mbox.channel); | 798 | if (bpmp->soc->ops->deinit) |
| 892 | cleanup_threaded_channels: | 799 | bpmp->soc->ops->deinit(bpmp); |
| 893 | for (i = 0; i < bpmp->threaded.count; i++) { | ||
| 894 | if (bpmp->threaded_channels[i].bpmp) | ||
| 895 | tegra_bpmp_channel_cleanup(&bpmp->threaded_channels[i]); | ||
| 896 | } | ||
| 897 | 800 | ||
| 898 | tegra_bpmp_channel_cleanup(bpmp->rx_channel); | ||
| 899 | cleanup_tx_channel: | ||
| 900 | tegra_bpmp_channel_cleanup(bpmp->tx_channel); | ||
| 901 | free_rx: | ||
| 902 | gen_pool_free(bpmp->rx.pool, (unsigned long)bpmp->rx.virt, 4096); | ||
| 903 | free_tx: | ||
| 904 | gen_pool_free(bpmp->tx.pool, (unsigned long)bpmp->tx.virt, 4096); | ||
| 905 | return err; | 801 | return err; |
| 906 | } | 802 | } |
| 907 | 803 | ||
| 908 | static int __maybe_unused tegra_bpmp_resume(struct device *dev) | 804 | static int __maybe_unused tegra_bpmp_resume(struct device *dev) |
| 909 | { | 805 | { |
| 910 | struct tegra_bpmp *bpmp = dev_get_drvdata(dev); | 806 | struct tegra_bpmp *bpmp = dev_get_drvdata(dev); |
| 911 | unsigned int i; | ||
| 912 | |||
| 913 | /* reset message channels */ | ||
| 914 | tegra_bpmp_channel_reset(bpmp->tx_channel); | ||
| 915 | tegra_bpmp_channel_reset(bpmp->rx_channel); | ||
| 916 | |||
| 917 | for (i = 0; i < bpmp->threaded.count; i++) | ||
| 918 | tegra_bpmp_channel_reset(&bpmp->threaded_channels[i]); | ||
| 919 | 807 | ||
| 920 | return 0; | 808 | if (bpmp->soc->ops->resume) |
| 809 | return bpmp->soc->ops->resume(bpmp); | ||
| 810 | else | ||
| 811 | return 0; | ||
| 921 | } | 812 | } |
| 922 | 813 | ||
| 923 | static SIMPLE_DEV_PM_OPS(tegra_bpmp_pm_ops, NULL, tegra_bpmp_resume); | 814 | static SIMPLE_DEV_PM_OPS(tegra_bpmp_pm_ops, NULL, tegra_bpmp_resume); |
| 924 | 815 | ||
| 816 | #if IS_ENABLED(CONFIG_ARCH_TEGRA_186_SOC) || \ | ||
| 817 | IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC) | ||
| 925 | static const struct tegra_bpmp_soc tegra186_soc = { | 818 | static const struct tegra_bpmp_soc tegra186_soc = { |
| 926 | .channels = { | 819 | .channels = { |
| 927 | .cpu_tx = { | 820 | .cpu_tx = { |
| @@ -938,11 +831,42 @@ static const struct tegra_bpmp_soc tegra186_soc = { | |||
| 938 | .timeout = 0, | 831 | .timeout = 0, |
| 939 | }, | 832 | }, |
| 940 | }, | 833 | }, |
| 834 | .ops = &tegra186_bpmp_ops, | ||
| 941 | .num_resets = 193, | 835 | .num_resets = 193, |
| 942 | }; | 836 | }; |
| 837 | #endif | ||
| 838 | |||
| 839 | #if IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC) | ||
| 840 | static const struct tegra_bpmp_soc tegra210_soc = { | ||
| 841 | .channels = { | ||
| 842 | .cpu_tx = { | ||
| 843 | .offset = 0, | ||
| 844 | .count = 1, | ||
| 845 | .timeout = 60 * USEC_PER_SEC, | ||
| 846 | }, | ||
| 847 | .thread = { | ||
| 848 | .offset = 4, | ||
| 849 | .count = 1, | ||
| 850 | .timeout = 600 * USEC_PER_SEC, | ||
| 851 | }, | ||
| 852 | .cpu_rx = { | ||
| 853 | .offset = 8, | ||
| 854 | .count = 1, | ||
| 855 | .timeout = 0, | ||
| 856 | }, | ||
| 857 | }, | ||
| 858 | .ops = &tegra210_bpmp_ops, | ||
| 859 | }; | ||
| 860 | #endif | ||
| 943 | 861 | ||
| 944 | static const struct of_device_id tegra_bpmp_match[] = { | 862 | static const struct of_device_id tegra_bpmp_match[] = { |
| 863 | #if IS_ENABLED(CONFIG_ARCH_TEGRA_186_SOC) || \ | ||
| 864 | IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC) | ||
| 945 | { .compatible = "nvidia,tegra186-bpmp", .data = &tegra186_soc }, | 865 | { .compatible = "nvidia,tegra186-bpmp", .data = &tegra186_soc }, |
| 866 | #endif | ||
| 867 | #if IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC) | ||
| 868 | { .compatible = "nvidia,tegra210-bpmp", .data = &tegra210_soc }, | ||
| 869 | #endif | ||
| 946 | { } | 870 | { } |
| 947 | }; | 871 | }; |
| 948 | 872 | ||
diff --git a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c index 69ed1464175c..3fbbb61012c4 100644 --- a/drivers/firmware/ti_sci.c +++ b/drivers/firmware/ti_sci.c | |||
| @@ -146,25 +146,8 @@ static int ti_sci_debug_show(struct seq_file *s, void *unused) | |||
| 146 | return 0; | 146 | return 0; |
| 147 | } | 147 | } |
| 148 | 148 | ||
| 149 | /** | 149 | /* Provide the log file operations interface*/ |
| 150 | * ti_sci_debug_open() - debug file open | 150 | DEFINE_SHOW_ATTRIBUTE(ti_sci_debug); |
| 151 | * @inode: inode pointer | ||
| 152 | * @file: file pointer | ||
| 153 | * | ||
| 154 | * Return: result of single_open | ||
| 155 | */ | ||
| 156 | static int ti_sci_debug_open(struct inode *inode, struct file *file) | ||
| 157 | { | ||
| 158 | return single_open(file, ti_sci_debug_show, inode->i_private); | ||
| 159 | } | ||
| 160 | |||
| 161 | /* log file operations */ | ||
| 162 | static const struct file_operations ti_sci_debug_fops = { | ||
| 163 | .open = ti_sci_debug_open, | ||
| 164 | .read = seq_read, | ||
| 165 | .llseek = seq_lseek, | ||
| 166 | .release = single_release, | ||
| 167 | }; | ||
| 168 | 151 | ||
| 169 | /** | 152 | /** |
| 170 | * ti_sci_debugfs_create() - Create log debug file | 153 | * ti_sci_debugfs_create() - Create log debug file |
diff --git a/drivers/firmware/xilinx/Kconfig b/drivers/firmware/xilinx/Kconfig index 8f44b9cd295a..bd33bbf70daf 100644 --- a/drivers/firmware/xilinx/Kconfig +++ b/drivers/firmware/xilinx/Kconfig | |||
| @@ -6,6 +6,7 @@ menu "Zynq MPSoC Firmware Drivers" | |||
| 6 | 6 | ||
| 7 | config ZYNQMP_FIRMWARE | 7 | config ZYNQMP_FIRMWARE |
| 8 | bool "Enable Xilinx Zynq MPSoC firmware interface" | 8 | bool "Enable Xilinx Zynq MPSoC firmware interface" |
| 9 | select MFD_CORE | ||
| 9 | help | 10 | help |
| 10 | Firmware interface driver is used by different | 11 | Firmware interface driver is used by different |
| 11 | drivers to communicate with the firmware for | 12 | drivers to communicate with the firmware for |
diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index 9a1c72a9280f..98f936125643 100644 --- a/drivers/firmware/xilinx/zynqmp.c +++ b/drivers/firmware/xilinx/zynqmp.c | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | #include <linux/compiler.h> | 14 | #include <linux/compiler.h> |
| 15 | #include <linux/device.h> | 15 | #include <linux/device.h> |
| 16 | #include <linux/init.h> | 16 | #include <linux/init.h> |
| 17 | #include <linux/mfd/core.h> | ||
| 17 | #include <linux/module.h> | 18 | #include <linux/module.h> |
| 18 | #include <linux/of.h> | 19 | #include <linux/of.h> |
| 19 | #include <linux/of_platform.h> | 20 | #include <linux/of_platform.h> |
| @@ -23,6 +24,12 @@ | |||
| 23 | #include <linux/firmware/xlnx-zynqmp.h> | 24 | #include <linux/firmware/xlnx-zynqmp.h> |
| 24 | #include "zynqmp-debug.h" | 25 | #include "zynqmp-debug.h" |
| 25 | 26 | ||
| 27 | static const struct mfd_cell firmware_devs[] = { | ||
| 28 | { | ||
| 29 | .name = "zynqmp_power_controller", | ||
| 30 | }, | ||
| 31 | }; | ||
| 32 | |||
| 26 | /** | 33 | /** |
| 27 | * zynqmp_pm_ret_code() - Convert PMU-FW error codes to Linux error codes | 34 | * zynqmp_pm_ret_code() - Convert PMU-FW error codes to Linux error codes |
| 28 | * @ret_status: PMUFW return code | 35 | * @ret_status: PMUFW return code |
| @@ -187,6 +194,29 @@ static int zynqmp_pm_get_api_version(u32 *version) | |||
| 187 | } | 194 | } |
| 188 | 195 | ||
| 189 | /** | 196 | /** |
| 197 | * zynqmp_pm_get_chipid - Get silicon ID registers | ||
| 198 | * @idcode: IDCODE register | ||
| 199 | * @version: version register | ||
| 200 | * | ||
| 201 | * Return: Returns the status of the operation and the idcode and version | ||
| 202 | * registers in @idcode and @version. | ||
| 203 | */ | ||
| 204 | static int zynqmp_pm_get_chipid(u32 *idcode, u32 *version) | ||
| 205 | { | ||
| 206 | u32 ret_payload[PAYLOAD_ARG_CNT]; | ||
| 207 | int ret; | ||
| 208 | |||
| 209 | if (!idcode || !version) | ||
| 210 | return -EINVAL; | ||
| 211 | |||
| 212 | ret = zynqmp_pm_invoke_fn(PM_GET_CHIPID, 0, 0, 0, 0, ret_payload); | ||
| 213 | *idcode = ret_payload[1]; | ||
| 214 | *version = ret_payload[2]; | ||
| 215 | |||
| 216 | return ret; | ||
| 217 | } | ||
| 218 | |||
| 219 | /** | ||
| 190 | * zynqmp_pm_get_trustzone_version() - Get secure trustzone firmware version | 220 | * zynqmp_pm_get_trustzone_version() - Get secure trustzone firmware version |
| 191 | * @version: Returned version value | 221 | * @version: Returned version value |
| 192 | * | 222 | * |
| @@ -469,8 +499,129 @@ static int zynqmp_pm_ioctl(u32 node_id, u32 ioctl_id, u32 arg1, u32 arg2, | |||
| 469 | arg1, arg2, out); | 499 | arg1, arg2, out); |
| 470 | } | 500 | } |
| 471 | 501 | ||
| 502 | /** | ||
| 503 | * zynqmp_pm_reset_assert - Request setting of reset (1 - assert, 0 - release) | ||
| 504 | * @reset: Reset to be configured | ||
| 505 | * @assert_flag: Flag stating should reset be asserted (1) or | ||
| 506 | * released (0) | ||
| 507 | * | ||
| 508 | * Return: Returns status, either success or error+reason | ||
| 509 | */ | ||
| 510 | static int zynqmp_pm_reset_assert(const enum zynqmp_pm_reset reset, | ||
| 511 | const enum zynqmp_pm_reset_action assert_flag) | ||
| 512 | { | ||
| 513 | return zynqmp_pm_invoke_fn(PM_RESET_ASSERT, reset, assert_flag, | ||
| 514 | 0, 0, NULL); | ||
| 515 | } | ||
| 516 | |||
| 517 | /** | ||
| 518 | * zynqmp_pm_reset_get_status - Get status of the reset | ||
| 519 | * @reset: Reset whose status should be returned | ||
| 520 | * @status: Returned status | ||
| 521 | * | ||
| 522 | * Return: Returns status, either success or error+reason | ||
| 523 | */ | ||
| 524 | static int zynqmp_pm_reset_get_status(const enum zynqmp_pm_reset reset, | ||
| 525 | u32 *status) | ||
| 526 | { | ||
| 527 | u32 ret_payload[PAYLOAD_ARG_CNT]; | ||
| 528 | int ret; | ||
| 529 | |||
| 530 | if (!status) | ||
| 531 | return -EINVAL; | ||
| 532 | |||
| 533 | ret = zynqmp_pm_invoke_fn(PM_RESET_GET_STATUS, reset, 0, | ||
| 534 | 0, 0, ret_payload); | ||
| 535 | *status = ret_payload[1]; | ||
| 536 | |||
| 537 | return ret; | ||
| 538 | } | ||
| 539 | |||
| 540 | /** | ||
| 541 | * zynqmp_pm_init_finalize() - PM call to inform firmware that the caller | ||
| 542 | * master has initialized its own power management | ||
| 543 | * | ||
| 544 | * This API function is to be used for notify the power management controller | ||
| 545 | * about the completed power management initialization. | ||
| 546 | * | ||
| 547 | * Return: Returns status, either success or error+reason | ||
| 548 | */ | ||
| 549 | static int zynqmp_pm_init_finalize(void) | ||
| 550 | { | ||
| 551 | return zynqmp_pm_invoke_fn(PM_PM_INIT_FINALIZE, 0, 0, 0, 0, NULL); | ||
| 552 | } | ||
| 553 | |||
| 554 | /** | ||
| 555 | * zynqmp_pm_set_suspend_mode() - Set system suspend mode | ||
| 556 | * @mode: Mode to set for system suspend | ||
| 557 | * | ||
| 558 | * This API function is used to set mode of system suspend. | ||
| 559 | * | ||
| 560 | * Return: Returns status, either success or error+reason | ||
| 561 | */ | ||
| 562 | static int zynqmp_pm_set_suspend_mode(u32 mode) | ||
| 563 | { | ||
| 564 | return zynqmp_pm_invoke_fn(PM_SET_SUSPEND_MODE, mode, 0, 0, 0, NULL); | ||
| 565 | } | ||
| 566 | |||
| 567 | /** | ||
| 568 | * zynqmp_pm_request_node() - Request a node with specific capabilities | ||
| 569 | * @node: Node ID of the slave | ||
| 570 | * @capabilities: Requested capabilities of the slave | ||
| 571 | * @qos: Quality of service (not supported) | ||
| 572 | * @ack: Flag to specify whether acknowledge is requested | ||
| 573 | * | ||
| 574 | * This function is used by master to request particular node from firmware. | ||
| 575 | * Every master must request node before using it. | ||
| 576 | * | ||
| 577 | * Return: Returns status, either success or error+reason | ||
| 578 | */ | ||
| 579 | static int zynqmp_pm_request_node(const u32 node, const u32 capabilities, | ||
| 580 | const u32 qos, | ||
| 581 | const enum zynqmp_pm_request_ack ack) | ||
| 582 | { | ||
| 583 | return zynqmp_pm_invoke_fn(PM_REQUEST_NODE, node, capabilities, | ||
| 584 | qos, ack, NULL); | ||
| 585 | } | ||
| 586 | |||
| 587 | /** | ||
| 588 | * zynqmp_pm_release_node() - Release a node | ||
| 589 | * @node: Node ID of the slave | ||
| 590 | * | ||
| 591 | * This function is used by master to inform firmware that master | ||
| 592 | * has released node. Once released, master must not use that node | ||
| 593 | * without re-request. | ||
| 594 | * | ||
| 595 | * Return: Returns status, either success or error+reason | ||
| 596 | */ | ||
| 597 | static int zynqmp_pm_release_node(const u32 node) | ||
| 598 | { | ||
| 599 | return zynqmp_pm_invoke_fn(PM_RELEASE_NODE, node, 0, 0, 0, NULL); | ||
| 600 | } | ||
| 601 | |||
| 602 | /** | ||
| 603 | * zynqmp_pm_set_requirement() - PM call to set requirement for PM slaves | ||
| 604 | * @node: Node ID of the slave | ||
| 605 | * @capabilities: Requested capabilities of the slave | ||
| 606 | * @qos: Quality of service (not supported) | ||
| 607 | * @ack: Flag to specify whether acknowledge is requested | ||
| 608 | * | ||
| 609 | * This API function is to be used for slaves a PU already has requested | ||
| 610 | * to change its capabilities. | ||
| 611 | * | ||
| 612 | * Return: Returns status, either success or error+reason | ||
| 613 | */ | ||
| 614 | static int zynqmp_pm_set_requirement(const u32 node, const u32 capabilities, | ||
| 615 | const u32 qos, | ||
| 616 | const enum zynqmp_pm_request_ack ack) | ||
| 617 | { | ||
| 618 | return zynqmp_pm_invoke_fn(PM_SET_REQUIREMENT, node, capabilities, | ||
| 619 | qos, ack, NULL); | ||
| 620 | } | ||
| 621 | |||
| 472 | static const struct zynqmp_eemi_ops eemi_ops = { | 622 | static const struct zynqmp_eemi_ops eemi_ops = { |
| 473 | .get_api_version = zynqmp_pm_get_api_version, | 623 | .get_api_version = zynqmp_pm_get_api_version, |
| 624 | .get_chipid = zynqmp_pm_get_chipid, | ||
| 474 | .query_data = zynqmp_pm_query_data, | 625 | .query_data = zynqmp_pm_query_data, |
| 475 | .clock_enable = zynqmp_pm_clock_enable, | 626 | .clock_enable = zynqmp_pm_clock_enable, |
| 476 | .clock_disable = zynqmp_pm_clock_disable, | 627 | .clock_disable = zynqmp_pm_clock_disable, |
| @@ -482,6 +633,13 @@ static const struct zynqmp_eemi_ops eemi_ops = { | |||
| 482 | .clock_setparent = zynqmp_pm_clock_setparent, | 633 | .clock_setparent = zynqmp_pm_clock_setparent, |
| 483 | .clock_getparent = zynqmp_pm_clock_getparent, | 634 | .clock_getparent = zynqmp_pm_clock_getparent, |
| 484 | .ioctl = zynqmp_pm_ioctl, | 635 | .ioctl = zynqmp_pm_ioctl, |
| 636 | .reset_assert = zynqmp_pm_reset_assert, | ||
| 637 | .reset_get_status = zynqmp_pm_reset_get_status, | ||
| 638 | .init_finalize = zynqmp_pm_init_finalize, | ||
| 639 | .set_suspend_mode = zynqmp_pm_set_suspend_mode, | ||
| 640 | .request_node = zynqmp_pm_request_node, | ||
| 641 | .release_node = zynqmp_pm_release_node, | ||
| 642 | .set_requirement = zynqmp_pm_set_requirement, | ||
| 485 | }; | 643 | }; |
| 486 | 644 | ||
| 487 | /** | 645 | /** |
| @@ -538,11 +696,19 @@ static int zynqmp_firmware_probe(struct platform_device *pdev) | |||
| 538 | 696 | ||
| 539 | zynqmp_pm_api_debugfs_init(); | 697 | zynqmp_pm_api_debugfs_init(); |
| 540 | 698 | ||
| 699 | ret = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE, firmware_devs, | ||
| 700 | ARRAY_SIZE(firmware_devs), NULL, 0, NULL); | ||
| 701 | if (ret) { | ||
| 702 | dev_err(&pdev->dev, "failed to add MFD devices %d\n", ret); | ||
| 703 | return ret; | ||
| 704 | } | ||
| 705 | |||
| 541 | return of_platform_populate(dev->of_node, NULL, NULL, dev); | 706 | return of_platform_populate(dev->of_node, NULL, NULL, dev); |
| 542 | } | 707 | } |
| 543 | 708 | ||
| 544 | static int zynqmp_firmware_remove(struct platform_device *pdev) | 709 | static int zynqmp_firmware_remove(struct platform_device *pdev) |
| 545 | { | 710 | { |
| 711 | mfd_remove_devices(&pdev->dev); | ||
| 546 | zynqmp_pm_api_debugfs_exit(); | 712 | zynqmp_pm_api_debugfs_exit(); |
| 547 | 713 | ||
| 548 | return 0; | 714 | return 0; |
