diff options
| author | Timo Alho <talho@nvidia.com> | 2019-01-24 12:03:53 -0500 |
|---|---|---|
| committer | Thierry Reding <treding@nvidia.com> | 2019-01-25 09:58:47 -0500 |
| commit | cdfa358b248efd36c6a9cb4d4d0a3ba7509f8387 (patch) | |
| tree | db7d882df0d9233280ad0e4e300d3d3794f2b3f2 /drivers/firmware | |
| parent | 165ce6e01d2d00bff2f10eba8dd679cee44163bd (diff) | |
firmware: tegra: Refactor BPMP driver
Split BPMP driver into common and chip specific parts to facilitate
adding support for previous and future Tegra chips that are using BPMP
as co-processor.
Signed-off-by: Timo Alho <talho@nvidia.com>
Acked-by: Jon Hunter <jonathanh@nvidia.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>
Diffstat (limited to 'drivers/firmware')
| -rw-r--r-- | drivers/firmware/tegra/Makefile | 1 | ||||
| -rw-r--r-- | drivers/firmware/tegra/bpmp-private.h | 28 | ||||
| -rw-r--r-- | drivers/firmware/tegra/bpmp-tegra186.c | 310 | ||||
| -rw-r--r-- | drivers/firmware/tegra/bpmp.c | 257 |
4 files changed, 394 insertions, 202 deletions
diff --git a/drivers/firmware/tegra/Makefile b/drivers/firmware/tegra/Makefile index 1b826dcca719..559355674bca 100644 --- a/drivers/firmware/tegra/Makefile +++ b/drivers/firmware/tegra/Makefile | |||
| @@ -1,4 +1,5 @@ | |||
| 1 | tegra-bpmp-y = bpmp.o | 1 | tegra-bpmp-y = bpmp.o |
| 2 | tegra-bpmp-$(CONFIG_ARCH_TEGRA_186_SOC) += bpmp-tegra186.o | ||
| 2 | tegra-bpmp-$(CONFIG_DEBUG_FS) += bpmp-debugfs.o | 3 | tegra-bpmp-$(CONFIG_DEBUG_FS) += bpmp-debugfs.o |
| 3 | obj-$(CONFIG_TEGRA_BPMP) += tegra-bpmp.o | 4 | obj-$(CONFIG_TEGRA_BPMP) += tegra-bpmp.o |
| 4 | obj-$(CONFIG_TEGRA_IVC) += ivc.o | 5 | 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..6fb10f1cfb8f --- /dev/null +++ b/drivers/firmware/tegra/bpmp-private.h | |||
| @@ -0,0 +1,28 @@ | |||
| 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 | extern const struct tegra_bpmp_ops tegra186_bpmp_ops; | ||
| 27 | |||
| 28 | #endif | ||
diff --git a/drivers/firmware/tegra/bpmp-tegra186.c b/drivers/firmware/tegra/bpmp-tegra186.c new file mode 100644 index 000000000000..89bb32b97345 --- /dev/null +++ b/drivers/firmware/tegra/bpmp-tegra186.c | |||
| @@ -0,0 +1,310 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* | ||
| 3 | * Copyright (c) 2018, NVIDIA CORPORATION. | ||
| 4 | */ | ||
| 5 | |||
| 6 | #include <linux/clk/tegra.h> | ||
| 7 | #include <linux/genalloc.h> | ||
| 8 | #include <linux/mailbox_client.h> | ||
| 9 | #include <linux/of.h> | ||
| 10 | #include <linux/of_address.h> | ||
| 11 | #include <linux/of_device.h> | ||
| 12 | #include <linux/platform_device.h> | ||
| 13 | #include <linux/semaphore.h> | ||
| 14 | |||
| 15 | #include <soc/tegra/bpmp.h> | ||
| 16 | #include <soc/tegra/bpmp-abi.h> | ||
| 17 | #include <soc/tegra/ivc.h> | ||
| 18 | |||
| 19 | #include "bpmp-private.h" | ||
| 20 | |||
| 21 | struct tegra186_bpmp { | ||
| 22 | struct tegra_bpmp *parent; | ||
| 23 | |||
| 24 | struct { | ||
| 25 | struct gen_pool *pool; | ||
| 26 | dma_addr_t phys; | ||
| 27 | void *virt; | ||
| 28 | } tx, rx; | ||
| 29 | |||
| 30 | struct { | ||
| 31 | struct mbox_client client; | ||
| 32 | struct mbox_chan *channel; | ||
| 33 | } mbox; | ||
| 34 | }; | ||
| 35 | |||
| 36 | static inline struct tegra_bpmp * | ||
| 37 | mbox_client_to_bpmp(struct mbox_client *client) | ||
| 38 | { | ||
| 39 | struct tegra186_bpmp *priv; | ||
| 40 | |||
| 41 | priv = container_of(client, struct tegra186_bpmp, mbox.client); | ||
| 42 | |||
| 43 | return priv->parent; | ||
| 44 | } | ||
| 45 | |||
| 46 | static bool tegra186_bpmp_is_message_ready(struct tegra_bpmp_channel *channel) | ||
| 47 | { | ||
| 48 | void *frame; | ||
| 49 | |||
| 50 | frame = tegra_ivc_read_get_next_frame(channel->ivc); | ||
| 51 | if (IS_ERR(frame)) { | ||
| 52 | channel->ib = NULL; | ||
| 53 | return false; | ||
| 54 | } | ||
| 55 | |||
| 56 | channel->ib = frame; | ||
| 57 | |||
| 58 | return true; | ||
| 59 | } | ||
| 60 | |||
| 61 | static bool tegra186_bpmp_is_channel_free(struct tegra_bpmp_channel *channel) | ||
| 62 | { | ||
| 63 | void *frame; | ||
| 64 | |||
| 65 | frame = tegra_ivc_write_get_next_frame(channel->ivc); | ||
| 66 | if (IS_ERR(frame)) { | ||
| 67 | channel->ob = NULL; | ||
| 68 | return false; | ||
| 69 | } | ||
| 70 | |||
| 71 | channel->ob = frame; | ||
| 72 | |||
| 73 | return true; | ||
| 74 | } | ||
| 75 | |||
| 76 | static int tegra186_bpmp_ack_message(struct tegra_bpmp_channel *channel) | ||
| 77 | { | ||
| 78 | return tegra_ivc_read_advance(channel->ivc); | ||
| 79 | } | ||
| 80 | |||
| 81 | static int tegra186_bpmp_post_message(struct tegra_bpmp_channel *channel) | ||
| 82 | { | ||
| 83 | return tegra_ivc_write_advance(channel->ivc); | ||
| 84 | } | ||
| 85 | |||
| 86 | static int tegra186_bpmp_ring_doorbell(struct tegra_bpmp *bpmp) | ||
| 87 | { | ||
| 88 | struct tegra186_bpmp *priv = bpmp->priv; | ||
| 89 | int err; | ||
| 90 | |||
| 91 | err = mbox_send_message(priv->mbox.channel, NULL); | ||
| 92 | if (err < 0) | ||
| 93 | return err; | ||
| 94 | |||
| 95 | mbox_client_txdone(priv->mbox.channel, 0); | ||
| 96 | |||
| 97 | return 0; | ||
| 98 | } | ||
| 99 | |||
| 100 | static void tegra186_bpmp_ivc_notify(struct tegra_ivc *ivc, void *data) | ||
| 101 | { | ||
| 102 | struct tegra_bpmp *bpmp = data; | ||
| 103 | struct tegra186_bpmp *priv = bpmp->priv; | ||
| 104 | |||
| 105 | if (WARN_ON(priv->mbox.channel == NULL)) | ||
| 106 | return; | ||
| 107 | |||
| 108 | tegra186_bpmp_ring_doorbell(bpmp); | ||
| 109 | } | ||
| 110 | |||
| 111 | static int tegra186_bpmp_channel_init(struct tegra_bpmp_channel *channel, | ||
| 112 | struct tegra_bpmp *bpmp, | ||
| 113 | unsigned int index) | ||
| 114 | { | ||
| 115 | struct tegra186_bpmp *priv = bpmp->priv; | ||
| 116 | size_t message_size, queue_size; | ||
| 117 | unsigned int offset; | ||
| 118 | int err; | ||
| 119 | |||
| 120 | channel->ivc = devm_kzalloc(bpmp->dev, sizeof(*channel->ivc), | ||
| 121 | GFP_KERNEL); | ||
| 122 | if (!channel->ivc) | ||
| 123 | return -ENOMEM; | ||
| 124 | |||
| 125 | message_size = tegra_ivc_align(MSG_MIN_SZ); | ||
| 126 | queue_size = tegra_ivc_total_queue_size(message_size); | ||
| 127 | offset = queue_size * index; | ||
| 128 | |||
| 129 | err = tegra_ivc_init(channel->ivc, NULL, | ||
| 130 | priv->rx.virt + offset, priv->rx.phys + offset, | ||
| 131 | priv->tx.virt + offset, priv->tx.phys + offset, | ||
| 132 | 1, message_size, tegra186_bpmp_ivc_notify, | ||
| 133 | bpmp); | ||
| 134 | if (err < 0) { | ||
| 135 | dev_err(bpmp->dev, "failed to setup IVC for channel %u: %d\n", | ||
| 136 | index, err); | ||
| 137 | return err; | ||
| 138 | } | ||
| 139 | |||
| 140 | init_completion(&channel->completion); | ||
| 141 | channel->bpmp = bpmp; | ||
| 142 | |||
| 143 | return 0; | ||
| 144 | } | ||
| 145 | |||
| 146 | static void tegra186_bpmp_channel_reset(struct tegra_bpmp_channel *channel) | ||
| 147 | { | ||
| 148 | /* reset the channel state */ | ||
| 149 | tegra_ivc_reset(channel->ivc); | ||
| 150 | |||
| 151 | /* sync the channel state with BPMP */ | ||
| 152 | while (tegra_ivc_notified(channel->ivc)) | ||
| 153 | ; | ||
| 154 | } | ||
| 155 | |||
| 156 | static void tegra186_bpmp_channel_cleanup(struct tegra_bpmp_channel *channel) | ||
| 157 | { | ||
| 158 | tegra_ivc_cleanup(channel->ivc); | ||
| 159 | } | ||
| 160 | |||
| 161 | static void mbox_handle_rx(struct mbox_client *client, void *data) | ||
| 162 | { | ||
| 163 | struct tegra_bpmp *bpmp = mbox_client_to_bpmp(client); | ||
| 164 | |||
| 165 | tegra_bpmp_handle_rx(bpmp); | ||
| 166 | } | ||
| 167 | |||
| 168 | static int tegra186_bpmp_init(struct tegra_bpmp *bpmp) | ||
| 169 | { | ||
| 170 | struct tegra186_bpmp *priv; | ||
| 171 | unsigned int i; | ||
| 172 | int err; | ||
| 173 | |||
| 174 | priv = devm_kzalloc(bpmp->dev, sizeof(*priv), GFP_KERNEL); | ||
| 175 | if (!priv) | ||
| 176 | return -ENOMEM; | ||
| 177 | |||
| 178 | bpmp->priv = priv; | ||
| 179 | priv->parent = bpmp; | ||
| 180 | |||
| 181 | priv->tx.pool = of_gen_pool_get(bpmp->dev->of_node, "shmem", 0); | ||
| 182 | if (!priv->tx.pool) { | ||
| 183 | dev_err(bpmp->dev, "TX shmem pool not found\n"); | ||
| 184 | return -ENOMEM; | ||
| 185 | } | ||
| 186 | |||
| 187 | priv->tx.virt = gen_pool_dma_alloc(priv->tx.pool, 4096, &priv->tx.phys); | ||
| 188 | if (!priv->tx.virt) { | ||
| 189 | dev_err(bpmp->dev, "failed to allocate from TX pool\n"); | ||
| 190 | return -ENOMEM; | ||
| 191 | } | ||
| 192 | |||
| 193 | priv->rx.pool = of_gen_pool_get(bpmp->dev->of_node, "shmem", 1); | ||
| 194 | if (!priv->rx.pool) { | ||
| 195 | dev_err(bpmp->dev, "RX shmem pool not found\n"); | ||
| 196 | err = -ENOMEM; | ||
| 197 | goto free_tx; | ||
| 198 | } | ||
| 199 | |||
| 200 | priv->rx.virt = gen_pool_dma_alloc(priv->rx.pool, 4096, &priv->rx.phys); | ||
| 201 | if (!priv->rx.virt) { | ||
| 202 | dev_err(bpmp->dev, "failed to allocate from RX pool\n"); | ||
| 203 | err = -ENOMEM; | ||
| 204 | goto free_tx; | ||
| 205 | } | ||
| 206 | |||
| 207 | err = tegra186_bpmp_channel_init(bpmp->tx_channel, bpmp, | ||
| 208 | bpmp->soc->channels.cpu_tx.offset); | ||
| 209 | if (err < 0) | ||
| 210 | goto free_rx; | ||
| 211 | |||
| 212 | err = tegra186_bpmp_channel_init(bpmp->rx_channel, bpmp, | ||
| 213 | bpmp->soc->channels.cpu_rx.offset); | ||
| 214 | if (err < 0) | ||
| 215 | goto cleanup_tx_channel; | ||
| 216 | |||
| 217 | for (i = 0; i < bpmp->threaded.count; i++) { | ||
| 218 | unsigned int index = bpmp->soc->channels.thread.offset + i; | ||
| 219 | |||
| 220 | err = tegra186_bpmp_channel_init(&bpmp->threaded_channels[i], | ||
| 221 | bpmp, index); | ||
| 222 | if (err < 0) | ||
| 223 | goto cleanup_channels; | ||
| 224 | } | ||
| 225 | |||
| 226 | /* mbox registration */ | ||
| 227 | priv->mbox.client.dev = bpmp->dev; | ||
| 228 | priv->mbox.client.rx_callback = mbox_handle_rx; | ||
| 229 | priv->mbox.client.tx_block = false; | ||
| 230 | priv->mbox.client.knows_txdone = false; | ||
| 231 | |||
| 232 | priv->mbox.channel = mbox_request_channel(&priv->mbox.client, 0); | ||
| 233 | if (IS_ERR(priv->mbox.channel)) { | ||
| 234 | err = PTR_ERR(priv->mbox.channel); | ||
| 235 | dev_err(bpmp->dev, "failed to get HSP mailbox: %d\n", err); | ||
| 236 | goto cleanup_channels; | ||
| 237 | } | ||
| 238 | |||
| 239 | tegra186_bpmp_channel_reset(bpmp->tx_channel); | ||
| 240 | tegra186_bpmp_channel_reset(bpmp->rx_channel); | ||
| 241 | |||
| 242 | for (i = 0; i < bpmp->threaded.count; i++) | ||
| 243 | tegra186_bpmp_channel_reset(&bpmp->threaded_channels[i]); | ||
| 244 | |||
| 245 | return 0; | ||
| 246 | |||
| 247 | cleanup_channels: | ||
| 248 | for (i = 0; i < bpmp->threaded.count; i++) { | ||
| 249 | if (!bpmp->threaded_channels[i].bpmp) | ||
| 250 | continue; | ||
| 251 | |||
| 252 | tegra186_bpmp_channel_cleanup(&bpmp->threaded_channels[i]); | ||
| 253 | } | ||
| 254 | |||
| 255 | tegra186_bpmp_channel_cleanup(bpmp->rx_channel); | ||
| 256 | cleanup_tx_channel: | ||
| 257 | tegra186_bpmp_channel_cleanup(bpmp->tx_channel); | ||
| 258 | free_rx: | ||
| 259 | gen_pool_free(priv->rx.pool, (unsigned long)priv->rx.virt, 4096); | ||
| 260 | free_tx: | ||
| 261 | gen_pool_free(priv->tx.pool, (unsigned long)priv->tx.virt, 4096); | ||
| 262 | |||
| 263 | return err; | ||
| 264 | } | ||
| 265 | |||
| 266 | static void tegra186_bpmp_deinit(struct tegra_bpmp *bpmp) | ||
| 267 | { | ||
| 268 | struct tegra186_bpmp *priv = bpmp->priv; | ||
| 269 | unsigned int i; | ||
| 270 | |||
| 271 | mbox_free_channel(priv->mbox.channel); | ||
| 272 | |||
| 273 | for (i = 0; i < bpmp->threaded.count; i++) | ||
| 274 | tegra186_bpmp_channel_cleanup(&bpmp->threaded_channels[i]); | ||
| 275 | |||
| 276 | tegra186_bpmp_channel_cleanup(bpmp->rx_channel); | ||
| 277 | tegra186_bpmp_channel_cleanup(bpmp->tx_channel); | ||
| 278 | |||
| 279 | gen_pool_free(priv->rx.pool, (unsigned long)priv->rx.virt, 4096); | ||
| 280 | gen_pool_free(priv->tx.pool, (unsigned long)priv->tx.virt, 4096); | ||
| 281 | } | ||
| 282 | |||
| 283 | static int tegra186_bpmp_resume(struct tegra_bpmp *bpmp) | ||
| 284 | { | ||
| 285 | unsigned int i; | ||
| 286 | |||
| 287 | /* reset message channels */ | ||
| 288 | tegra186_bpmp_channel_reset(bpmp->tx_channel); | ||
| 289 | tegra186_bpmp_channel_reset(bpmp->rx_channel); | ||
| 290 | |||
| 291 | for (i = 0; i < bpmp->threaded.count; i++) | ||
| 292 | tegra186_bpmp_channel_reset(&bpmp->threaded_channels[i]); | ||
| 293 | |||
| 294 | return 0; | ||
| 295 | } | ||
| 296 | |||
| 297 | const struct tegra_bpmp_ops tegra186_bpmp_ops = { | ||
| 298 | .init = tegra186_bpmp_init, | ||
| 299 | .deinit = tegra186_bpmp_deinit, | ||
| 300 | .is_response_ready = tegra186_bpmp_is_message_ready, | ||
| 301 | .is_request_ready = tegra186_bpmp_is_message_ready, | ||
| 302 | .ack_response = tegra186_bpmp_ack_message, | ||
| 303 | .ack_request = tegra186_bpmp_ack_message, | ||
| 304 | .is_response_channel_free = tegra186_bpmp_is_channel_free, | ||
| 305 | .is_request_channel_free = tegra186_bpmp_is_channel_free, | ||
| 306 | .post_response = tegra186_bpmp_post_message, | ||
| 307 | .post_request = tegra186_bpmp_post_message, | ||
| 308 | .ring_doorbell = tegra186_bpmp_ring_doorbell, | ||
| 309 | .resume = tegra186_bpmp_resume, | ||
| 310 | }; | ||
diff --git a/drivers/firmware/tegra/bpmp.c b/drivers/firmware/tegra/bpmp.c index e7fd68ebedd0..2b498deacdbc 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; |
| @@ -98,22 +108,16 @@ static bool tegra_bpmp_message_valid(const struct tegra_bpmp_message *msg) | |||
| 98 | 108 | ||
| 99 | static bool tegra_bpmp_is_response_ready(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)) { | ||
| 105 | channel->ib = NULL; | ||
| 106 | return false; | ||
| 107 | } | ||
| 108 | |||
| 109 | channel->ib = frame; | ||
| 110 | |||
| 111 | return true; | ||
| 112 | } | 114 | } |
| 113 | 115 | ||
| 114 | static bool tegra_bpmp_is_request_ready(struct tegra_bpmp_channel *channel) | 116 | static bool tegra_bpmp_is_request_ready(struct tegra_bpmp_channel *channel) |
| 115 | { | 117 | { |
| 116 | return tegra_bpmp_is_response_ready(channel); | 118 | const struct tegra_bpmp_ops *ops = channel_to_ops(channel); |
| 119 | |||
| 120 | return ops->is_request_ready(channel); | ||
| 117 | } | 121 | } |
| 118 | 122 | ||
| 119 | static int tegra_bpmp_wait_response(struct tegra_bpmp_channel *channel) | 123 | static int tegra_bpmp_wait_response(struct tegra_bpmp_channel *channel) |
| @@ -133,34 +137,32 @@ static int tegra_bpmp_wait_response(struct tegra_bpmp_channel *channel) | |||
| 133 | 137 | ||
| 134 | static int tegra_bpmp_ack_response(struct tegra_bpmp_channel *channel) | 138 | static int tegra_bpmp_ack_response(struct tegra_bpmp_channel *channel) |
| 135 | { | 139 | { |
| 136 | return tegra_ivc_read_advance(channel->ivc); | 140 | const struct tegra_bpmp_ops *ops = channel_to_ops(channel); |
| 141 | |||
| 142 | return ops->ack_response(channel); | ||
| 137 | } | 143 | } |
| 138 | 144 | ||
| 139 | static int tegra_bpmp_ack_request(struct tegra_bpmp_channel *channel) | 145 | static int tegra_bpmp_ack_request(struct tegra_bpmp_channel *channel) |
| 140 | { | 146 | { |
| 141 | return tegra_ivc_read_advance(channel->ivc); | 147 | const struct tegra_bpmp_ops *ops = channel_to_ops(channel); |
| 148 | |||
| 149 | return ops->ack_request(channel); | ||
| 142 | } | 150 | } |
| 143 | 151 | ||
| 144 | static bool | 152 | static bool |
| 145 | tegra_bpmp_is_request_channel_free(struct tegra_bpmp_channel *channel) | 153 | tegra_bpmp_is_request_channel_free(struct tegra_bpmp_channel *channel) |
| 146 | { | 154 | { |
| 147 | void *frame; | 155 | const struct tegra_bpmp_ops *ops = channel_to_ops(channel); |
| 148 | |||
| 149 | frame = tegra_ivc_write_get_next_frame(channel->ivc); | ||
| 150 | if (IS_ERR(frame)) { | ||
| 151 | channel->ob = NULL; | ||
| 152 | return false; | ||
| 153 | } | ||
| 154 | |||
| 155 | channel->ob = frame; | ||
| 156 | 156 | ||
| 157 | return true; | 157 | return ops->is_request_channel_free(channel); |
| 158 | } | 158 | } |
| 159 | 159 | ||
| 160 | static bool | 160 | static bool |
| 161 | tegra_bpmp_is_response_channel_free(struct tegra_bpmp_channel *channel) | 161 | tegra_bpmp_is_response_channel_free(struct tegra_bpmp_channel *channel) |
| 162 | { | 162 | { |
| 163 | return tegra_bpmp_is_request_channel_free(channel); | 163 | const struct tegra_bpmp_ops *ops = channel_to_ops(channel); |
| 164 | |||
| 165 | return ops->is_response_channel_free(channel); | ||
| 164 | } | 166 | } |
| 165 | 167 | ||
| 166 | static int | 168 | static int |
| @@ -183,28 +185,21 @@ tegra_bpmp_wait_request_channel_free(struct tegra_bpmp_channel *channel) | |||
| 183 | 185 | ||
| 184 | static int tegra_bpmp_post_request(struct tegra_bpmp_channel *channel) | 186 | static int tegra_bpmp_post_request(struct tegra_bpmp_channel *channel) |
| 185 | { | 187 | { |
| 186 | return tegra_ivc_write_advance(channel->ivc); | 188 | const struct tegra_bpmp_ops *ops = channel_to_ops(channel); |
| 189 | |||
| 190 | return ops->post_request(channel); | ||
| 187 | } | 191 | } |
| 188 | 192 | ||
| 189 | static int tegra_bpmp_post_response(struct tegra_bpmp_channel *channel) | 193 | static int tegra_bpmp_post_response(struct tegra_bpmp_channel *channel) |
| 190 | { | 194 | { |
| 191 | return tegra_ivc_write_advance(channel->ivc); | 195 | const struct tegra_bpmp_ops *ops = channel_to_ops(channel); |
| 196 | |||
| 197 | return ops->post_response(channel); | ||
| 192 | } | 198 | } |
| 193 | 199 | ||
| 194 | static int tegra_bpmp_ring_doorbell(struct tegra_bpmp *bpmp) | 200 | static int tegra_bpmp_ring_doorbell(struct tegra_bpmp *bpmp) |
| 195 | { | 201 | { |
| 196 | int err; | 202 | return bpmp->soc->ops->ring_doorbell(bpmp); |
| 197 | |||
| 198 | if (WARN_ON(bpmp->mbox.channel == NULL)) | ||
| 199 | return -EINVAL; | ||
| 200 | |||
| 201 | err = mbox_send_message(bpmp->mbox.channel, NULL); | ||
| 202 | if (err < 0) | ||
| 203 | return err; | ||
| 204 | |||
| 205 | mbox_client_txdone(bpmp->mbox.channel, 0); | ||
| 206 | |||
| 207 | return 0; | ||
| 208 | } | 203 | } |
| 209 | 204 | ||
| 210 | 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, |
| @@ -668,9 +663,8 @@ static void tegra_bpmp_channel_signal(struct tegra_bpmp_channel *channel) | |||
| 668 | complete(&channel->completion); | 663 | complete(&channel->completion); |
| 669 | } | 664 | } |
| 670 | 665 | ||
| 671 | static void tegra_bpmp_handle_rx(struct mbox_client *client, void *data) | 666 | void tegra_bpmp_handle_rx(struct tegra_bpmp *bpmp) |
| 672 | { | 667 | { |
| 673 | struct tegra_bpmp *bpmp = mbox_client_to_bpmp(client); | ||
| 674 | struct tegra_bpmp_channel *channel; | 668 | struct tegra_bpmp_channel *channel; |
| 675 | unsigned int i, count; | 669 | unsigned int i, count; |
| 676 | unsigned long *busy; | 670 | unsigned long *busy; |
| @@ -698,66 +692,9 @@ static void tegra_bpmp_handle_rx(struct mbox_client *client, void *data) | |||
| 698 | spin_unlock(&bpmp->lock); | 692 | spin_unlock(&bpmp->lock); |
| 699 | } | 693 | } |
| 700 | 694 | ||
| 701 | static void tegra_bpmp_ivc_notify(struct tegra_ivc *ivc, void *data) | ||
| 702 | { | ||
| 703 | struct tegra_bpmp *bpmp = data; | ||
| 704 | |||
| 705 | WARN_ON(tegra_bpmp_ring_doorbell(bpmp)); | ||
| 706 | } | ||
| 707 | |||
| 708 | static int tegra_bpmp_channel_init(struct tegra_bpmp_channel *channel, | ||
| 709 | struct tegra_bpmp *bpmp, | ||
| 710 | unsigned int index) | ||
| 711 | { | ||
| 712 | size_t message_size, queue_size; | ||
| 713 | unsigned int offset; | ||
| 714 | int err; | ||
| 715 | |||
| 716 | channel->ivc = devm_kzalloc(bpmp->dev, sizeof(*channel->ivc), | ||
| 717 | GFP_KERNEL); | ||
| 718 | if (!channel->ivc) | ||
| 719 | return -ENOMEM; | ||
| 720 | |||
| 721 | message_size = tegra_ivc_align(MSG_MIN_SZ); | ||
| 722 | queue_size = tegra_ivc_total_queue_size(message_size); | ||
| 723 | offset = queue_size * index; | ||
| 724 | |||
| 725 | err = tegra_ivc_init(channel->ivc, NULL, | ||
| 726 | bpmp->rx.virt + offset, bpmp->rx.phys + offset, | ||
| 727 | bpmp->tx.virt + offset, bpmp->tx.phys + offset, | ||
| 728 | 1, message_size, tegra_bpmp_ivc_notify, | ||
| 729 | bpmp); | ||
| 730 | if (err < 0) { | ||
| 731 | dev_err(bpmp->dev, "failed to setup IVC for channel %u: %d\n", | ||
| 732 | index, err); | ||
| 733 | return err; | ||
| 734 | } | ||
| 735 | |||
| 736 | init_completion(&channel->completion); | ||
| 737 | channel->bpmp = bpmp; | ||
| 738 | |||
| 739 | return 0; | ||
| 740 | } | ||
| 741 | |||
| 742 | static void tegra_bpmp_channel_reset(struct tegra_bpmp_channel *channel) | ||
| 743 | { | ||
| 744 | /* reset the channel state */ | ||
| 745 | tegra_ivc_reset(channel->ivc); | ||
| 746 | |||
| 747 | /* sync the channel state with BPMP */ | ||
| 748 | while (tegra_ivc_notified(channel->ivc)) | ||
| 749 | ; | ||
| 750 | } | ||
| 751 | |||
| 752 | static void tegra_bpmp_channel_cleanup(struct tegra_bpmp_channel *channel) | ||
| 753 | { | ||
| 754 | tegra_ivc_cleanup(channel->ivc); | ||
| 755 | } | ||
| 756 | |||
| 757 | static int tegra_bpmp_probe(struct platform_device *pdev) | 695 | static int tegra_bpmp_probe(struct platform_device *pdev) |
| 758 | { | 696 | { |
| 759 | struct tegra_bpmp *bpmp; | 697 | struct tegra_bpmp *bpmp; |
| 760 | unsigned int i; | ||
| 761 | char tag[TAG_SZ]; | 698 | char tag[TAG_SZ]; |
| 762 | size_t size; | 699 | size_t size; |
| 763 | int err; | 700 | int err; |
| @@ -769,32 +706,6 @@ static int tegra_bpmp_probe(struct platform_device *pdev) | |||
| 769 | bpmp->soc = of_device_get_match_data(&pdev->dev); | 706 | bpmp->soc = of_device_get_match_data(&pdev->dev); |
| 770 | bpmp->dev = &pdev->dev; | 707 | bpmp->dev = &pdev->dev; |
| 771 | 708 | ||
| 772 | bpmp->tx.pool = of_gen_pool_get(pdev->dev.of_node, "shmem", 0); | ||
| 773 | if (!bpmp->tx.pool) { | ||
| 774 | dev_err(&pdev->dev, "TX shmem pool not found\n"); | ||
| 775 | return -ENOMEM; | ||
| 776 | } | ||
| 777 | |||
| 778 | bpmp->tx.virt = gen_pool_dma_alloc(bpmp->tx.pool, 4096, &bpmp->tx.phys); | ||
| 779 | if (!bpmp->tx.virt) { | ||
| 780 | dev_err(&pdev->dev, "failed to allocate from TX pool\n"); | ||
| 781 | return -ENOMEM; | ||
| 782 | } | ||
| 783 | |||
| 784 | bpmp->rx.pool = of_gen_pool_get(pdev->dev.of_node, "shmem", 1); | ||
| 785 | if (!bpmp->rx.pool) { | ||
| 786 | dev_err(&pdev->dev, "RX shmem pool not found\n"); | ||
| 787 | err = -ENOMEM; | ||
| 788 | goto free_tx; | ||
| 789 | } | ||
| 790 | |||
| 791 | bpmp->rx.virt = gen_pool_dma_alloc(bpmp->rx.pool, 4096, &bpmp->rx.phys); | ||
| 792 | if (!bpmp->rx.virt) { | ||
| 793 | dev_err(&pdev->dev, "failed to allocate from RX pool\n"); | ||
| 794 | err = -ENOMEM; | ||
| 795 | goto free_tx; | ||
| 796 | } | ||
| 797 | |||
| 798 | INIT_LIST_HEAD(&bpmp->mrqs); | 709 | INIT_LIST_HEAD(&bpmp->mrqs); |
| 799 | spin_lock_init(&bpmp->lock); | 710 | spin_lock_init(&bpmp->lock); |
| 800 | 711 | ||
| @@ -804,81 +715,38 @@ static int tegra_bpmp_probe(struct platform_device *pdev) | |||
| 804 | size = BITS_TO_LONGS(bpmp->threaded.count) * sizeof(long); | 715 | size = BITS_TO_LONGS(bpmp->threaded.count) * sizeof(long); |
| 805 | 716 | ||
| 806 | bpmp->threaded.allocated = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); | 717 | bpmp->threaded.allocated = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); |
| 807 | if (!bpmp->threaded.allocated) { | 718 | if (!bpmp->threaded.allocated) |
| 808 | err = -ENOMEM; | 719 | return -ENOMEM; |
| 809 | goto free_rx; | ||
| 810 | } | ||
| 811 | 720 | ||
| 812 | bpmp->threaded.busy = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); | 721 | bpmp->threaded.busy = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); |
| 813 | if (!bpmp->threaded.busy) { | 722 | if (!bpmp->threaded.busy) |
| 814 | err = -ENOMEM; | 723 | return -ENOMEM; |
| 815 | goto free_rx; | ||
| 816 | } | ||
| 817 | 724 | ||
| 818 | spin_lock_init(&bpmp->atomic_tx_lock); | 725 | spin_lock_init(&bpmp->atomic_tx_lock); |
| 819 | bpmp->tx_channel = devm_kzalloc(&pdev->dev, sizeof(*bpmp->tx_channel), | 726 | bpmp->tx_channel = devm_kzalloc(&pdev->dev, sizeof(*bpmp->tx_channel), |
| 820 | GFP_KERNEL); | 727 | GFP_KERNEL); |
| 821 | if (!bpmp->tx_channel) { | 728 | if (!bpmp->tx_channel) |
| 822 | err = -ENOMEM; | 729 | return -ENOMEM; |
| 823 | goto free_rx; | ||
| 824 | } | ||
| 825 | 730 | ||
| 826 | bpmp->rx_channel = devm_kzalloc(&pdev->dev, sizeof(*bpmp->rx_channel), | 731 | bpmp->rx_channel = devm_kzalloc(&pdev->dev, sizeof(*bpmp->rx_channel), |
| 827 | GFP_KERNEL); | 732 | GFP_KERNEL); |
| 828 | if (!bpmp->rx_channel) { | 733 | if (!bpmp->rx_channel) |
| 829 | err = -ENOMEM; | 734 | return -ENOMEM; |
| 830 | goto free_rx; | ||
| 831 | } | ||
| 832 | 735 | ||
| 833 | bpmp->threaded_channels = devm_kcalloc(&pdev->dev, bpmp->threaded.count, | 736 | bpmp->threaded_channels = devm_kcalloc(&pdev->dev, bpmp->threaded.count, |
| 834 | sizeof(*bpmp->threaded_channels), | 737 | sizeof(*bpmp->threaded_channels), |
| 835 | GFP_KERNEL); | 738 | GFP_KERNEL); |
| 836 | if (!bpmp->threaded_channels) { | 739 | if (!bpmp->threaded_channels) |
| 837 | err = -ENOMEM; | 740 | return -ENOMEM; |
| 838 | goto free_rx; | ||
| 839 | } | ||
| 840 | |||
| 841 | err = tegra_bpmp_channel_init(bpmp->tx_channel, bpmp, | ||
| 842 | bpmp->soc->channels.cpu_tx.offset); | ||
| 843 | if (err < 0) | ||
| 844 | goto free_rx; | ||
| 845 | 741 | ||
| 846 | err = tegra_bpmp_channel_init(bpmp->rx_channel, bpmp, | 742 | err = bpmp->soc->ops->init(bpmp); |
| 847 | bpmp->soc->channels.cpu_rx.offset); | ||
| 848 | if (err < 0) | 743 | if (err < 0) |
| 849 | goto cleanup_tx_channel; | 744 | return err; |
| 850 | |||
| 851 | for (i = 0; i < bpmp->threaded.count; i++) { | ||
| 852 | err = tegra_bpmp_channel_init( | ||
| 853 | &bpmp->threaded_channels[i], bpmp, | ||
| 854 | bpmp->soc->channels.thread.offset + i); | ||
| 855 | if (err < 0) | ||
| 856 | goto cleanup_threaded_channels; | ||
| 857 | } | ||
| 858 | |||
| 859 | /* mbox registration */ | ||
| 860 | bpmp->mbox.client.dev = &pdev->dev; | ||
| 861 | bpmp->mbox.client.rx_callback = tegra_bpmp_handle_rx; | ||
| 862 | bpmp->mbox.client.tx_block = false; | ||
| 863 | bpmp->mbox.client.knows_txdone = false; | ||
| 864 | |||
| 865 | bpmp->mbox.channel = mbox_request_channel(&bpmp->mbox.client, 0); | ||
| 866 | if (IS_ERR(bpmp->mbox.channel)) { | ||
| 867 | err = PTR_ERR(bpmp->mbox.channel); | ||
| 868 | dev_err(&pdev->dev, "failed to get HSP mailbox: %d\n", err); | ||
| 869 | goto cleanup_threaded_channels; | ||
| 870 | } | ||
| 871 | |||
| 872 | /* reset message channels */ | ||
| 873 | tegra_bpmp_channel_reset(bpmp->tx_channel); | ||
| 874 | tegra_bpmp_channel_reset(bpmp->rx_channel); | ||
| 875 | for (i = 0; i < bpmp->threaded.count; i++) | ||
| 876 | tegra_bpmp_channel_reset(&bpmp->threaded_channels[i]); | ||
| 877 | 745 | ||
| 878 | err = tegra_bpmp_request_mrq(bpmp, MRQ_PING, | 746 | err = tegra_bpmp_request_mrq(bpmp, MRQ_PING, |
| 879 | tegra_bpmp_mrq_handle_ping, bpmp); | 747 | tegra_bpmp_mrq_handle_ping, bpmp); |
| 880 | if (err < 0) | 748 | if (err < 0) |
| 881 | goto free_mbox; | 749 | goto deinit; |
| 882 | 750 | ||
| 883 | err = tegra_bpmp_ping(bpmp); | 751 | err = tegra_bpmp_ping(bpmp); |
| 884 | if (err < 0) { | 752 | if (err < 0) { |
| @@ -920,37 +788,21 @@ static int tegra_bpmp_probe(struct platform_device *pdev) | |||
| 920 | 788 | ||
| 921 | free_mrq: | 789 | free_mrq: |
| 922 | tegra_bpmp_free_mrq(bpmp, MRQ_PING, bpmp); | 790 | tegra_bpmp_free_mrq(bpmp, MRQ_PING, bpmp); |
| 923 | free_mbox: | 791 | deinit: |
| 924 | mbox_free_channel(bpmp->mbox.channel); | 792 | if (bpmp->soc->ops->deinit) |
| 925 | cleanup_threaded_channels: | 793 | bpmp->soc->ops->deinit(bpmp); |
| 926 | for (i = 0; i < bpmp->threaded.count; i++) { | ||
| 927 | if (bpmp->threaded_channels[i].bpmp) | ||
| 928 | tegra_bpmp_channel_cleanup(&bpmp->threaded_channels[i]); | ||
| 929 | } | ||
| 930 | 794 | ||
| 931 | tegra_bpmp_channel_cleanup(bpmp->rx_channel); | ||
| 932 | cleanup_tx_channel: | ||
| 933 | tegra_bpmp_channel_cleanup(bpmp->tx_channel); | ||
| 934 | free_rx: | ||
| 935 | gen_pool_free(bpmp->rx.pool, (unsigned long)bpmp->rx.virt, 4096); | ||
| 936 | free_tx: | ||
| 937 | gen_pool_free(bpmp->tx.pool, (unsigned long)bpmp->tx.virt, 4096); | ||
| 938 | return err; | 795 | return err; |
| 939 | } | 796 | } |
| 940 | 797 | ||
| 941 | static int __maybe_unused tegra_bpmp_resume(struct device *dev) | 798 | static int __maybe_unused tegra_bpmp_resume(struct device *dev) |
| 942 | { | 799 | { |
| 943 | struct tegra_bpmp *bpmp = dev_get_drvdata(dev); | 800 | struct tegra_bpmp *bpmp = dev_get_drvdata(dev); |
| 944 | unsigned int i; | ||
| 945 | |||
| 946 | /* reset message channels */ | ||
| 947 | tegra_bpmp_channel_reset(bpmp->tx_channel); | ||
| 948 | tegra_bpmp_channel_reset(bpmp->rx_channel); | ||
| 949 | |||
| 950 | for (i = 0; i < bpmp->threaded.count; i++) | ||
| 951 | tegra_bpmp_channel_reset(&bpmp->threaded_channels[i]); | ||
| 952 | 801 | ||
| 953 | return 0; | 802 | if (bpmp->soc->ops->resume) |
| 803 | return bpmp->soc->ops->resume(bpmp); | ||
| 804 | else | ||
| 805 | return 0; | ||
| 954 | } | 806 | } |
| 955 | 807 | ||
| 956 | static SIMPLE_DEV_PM_OPS(tegra_bpmp_pm_ops, NULL, tegra_bpmp_resume); | 808 | static SIMPLE_DEV_PM_OPS(tegra_bpmp_pm_ops, NULL, tegra_bpmp_resume); |
| @@ -971,6 +823,7 @@ static const struct tegra_bpmp_soc tegra186_soc = { | |||
| 971 | .timeout = 0, | 823 | .timeout = 0, |
| 972 | }, | 824 | }, |
| 973 | }, | 825 | }, |
| 826 | .ops = &tegra186_bpmp_ops, | ||
| 974 | .num_resets = 193, | 827 | .num_resets = 193, |
| 975 | }; | 828 | }; |
| 976 | 829 | ||
