brintos

brintos / linux-shallow public Read only

0
0
Text · 8.8 KiB · 6f0d051 Raw
390 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (c) 2018, NVIDIA CORPORATION.4 */5 6#include <linux/genalloc.h>7#include <linux/io.h>8#include <linux/mailbox_client.h>9#include <linux/of_address.h>10#include <linux/platform_device.h>11 12#include <soc/tegra/bpmp.h>13#include <soc/tegra/bpmp-abi.h>14#include <soc/tegra/ivc.h>15 16#include "bpmp-private.h"17 18struct tegra186_bpmp {19	struct tegra_bpmp *parent;20 21	struct {22		struct gen_pool *pool;23		union {24			void __iomem *sram;25			void *dram;26		};27		dma_addr_t phys;28	} tx, rx;29 30	struct {31		struct mbox_client client;32		struct mbox_chan *channel;33	} mbox;34};35 36static inline struct tegra_bpmp *37mbox_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 46static bool tegra186_bpmp_is_message_ready(struct tegra_bpmp_channel *channel)47{48	int err;49 50	err = tegra_ivc_read_get_next_frame(channel->ivc, &channel->ib);51	if (err) {52		iosys_map_clear(&channel->ib);53		return false;54	}55 56	return true;57}58 59static bool tegra186_bpmp_is_channel_free(struct tegra_bpmp_channel *channel)60{61	int err;62 63	err = tegra_ivc_write_get_next_frame(channel->ivc, &channel->ob);64	if (err) {65		iosys_map_clear(&channel->ob);66		return false;67	}68 69	return true;70}71 72static int tegra186_bpmp_ack_message(struct tegra_bpmp_channel *channel)73{74	return tegra_ivc_read_advance(channel->ivc);75}76 77static int tegra186_bpmp_post_message(struct tegra_bpmp_channel *channel)78{79	return tegra_ivc_write_advance(channel->ivc);80}81 82static int tegra186_bpmp_ring_doorbell(struct tegra_bpmp *bpmp)83{84	struct tegra186_bpmp *priv = bpmp->priv;85	int err;86 87	err = mbox_send_message(priv->mbox.channel, NULL);88	if (err < 0)89		return err;90 91	mbox_client_txdone(priv->mbox.channel, 0);92 93	return 0;94}95 96static void tegra186_bpmp_ivc_notify(struct tegra_ivc *ivc, void *data)97{98	struct tegra_bpmp *bpmp = data;99	struct tegra186_bpmp *priv = bpmp->priv;100 101	if (WARN_ON(priv->mbox.channel == NULL))102		return;103 104	tegra186_bpmp_ring_doorbell(bpmp);105}106 107static int tegra186_bpmp_channel_init(struct tegra_bpmp_channel *channel,108				      struct tegra_bpmp *bpmp,109				      unsigned int index)110{111	struct tegra186_bpmp *priv = bpmp->priv;112	size_t message_size, queue_size;113	struct iosys_map rx, tx;114	unsigned int offset;115	int err;116 117	channel->ivc = devm_kzalloc(bpmp->dev, sizeof(*channel->ivc),118				    GFP_KERNEL);119	if (!channel->ivc)120		return -ENOMEM;121 122	message_size = tegra_ivc_align(MSG_MIN_SZ);123	queue_size = tegra_ivc_total_queue_size(message_size);124	offset = queue_size * index;125 126	if (priv->rx.pool) {127		iosys_map_set_vaddr_iomem(&rx, priv->rx.sram + offset);128		iosys_map_set_vaddr_iomem(&tx, priv->tx.sram + offset);129	} else {130		iosys_map_set_vaddr(&rx, priv->rx.dram + offset);131		iosys_map_set_vaddr(&tx, priv->tx.dram + offset);132	}133 134	err = tegra_ivc_init(channel->ivc, NULL, &rx, priv->rx.phys + offset, &tx,135			     priv->tx.phys + offset, 1, message_size, tegra186_bpmp_ivc_notify,136			     bpmp);137	if (err < 0) {138		dev_err(bpmp->dev, "failed to setup IVC for channel %u: %d\n",139			index, err);140		return err;141	}142 143	init_completion(&channel->completion);144	channel->bpmp = bpmp;145 146	return 0;147}148 149static void tegra186_bpmp_channel_reset(struct tegra_bpmp_channel *channel)150{151	/* reset the channel state */152	tegra_ivc_reset(channel->ivc);153 154	/* sync the channel state with BPMP */155	while (tegra_ivc_notified(channel->ivc))156		;157}158 159static void tegra186_bpmp_channel_cleanup(struct tegra_bpmp_channel *channel)160{161	tegra_ivc_cleanup(channel->ivc);162}163 164static void mbox_handle_rx(struct mbox_client *client, void *data)165{166	struct tegra_bpmp *bpmp = mbox_client_to_bpmp(client);167 168	tegra_bpmp_handle_rx(bpmp);169}170 171static void tegra186_bpmp_teardown_channels(struct tegra_bpmp *bpmp)172{173	struct tegra186_bpmp *priv = bpmp->priv;174	unsigned int i;175 176	for (i = 0; i < bpmp->threaded.count; i++) {177		if (!bpmp->threaded_channels[i].bpmp)178			continue;179 180		tegra186_bpmp_channel_cleanup(&bpmp->threaded_channels[i]);181	}182 183	tegra186_bpmp_channel_cleanup(bpmp->rx_channel);184	tegra186_bpmp_channel_cleanup(bpmp->tx_channel);185 186	if (priv->tx.pool) {187		gen_pool_free(priv->tx.pool, (unsigned long)priv->tx.sram, 4096);188		gen_pool_free(priv->rx.pool, (unsigned long)priv->rx.sram, 4096);189	}190}191 192static int tegra186_bpmp_dram_init(struct tegra_bpmp *bpmp)193{194	struct tegra186_bpmp *priv = bpmp->priv;195	struct device_node *np;196	struct resource res;197	size_t size;198	int err;199 200	np = of_parse_phandle(bpmp->dev->of_node, "memory-region", 0);201	if (!np)202		return -ENODEV;203 204	err = of_address_to_resource(np, 0, &res);205	if (err < 0) {206		dev_warn(bpmp->dev, "failed to parse memory region: %d\n", err);207		return err;208	}209 210	size = resource_size(&res);211 212	if (size < SZ_8K) {213		dev_warn(bpmp->dev, "DRAM region must be larger than 8 KiB\n");214		return -EINVAL;215	}216 217	priv->tx.phys = res.start;218	priv->rx.phys = res.start + SZ_4K;219 220	priv->tx.dram = devm_memremap(bpmp->dev, priv->tx.phys, size,221				      MEMREMAP_WC);222	if (IS_ERR(priv->tx.dram)) {223		err = PTR_ERR(priv->tx.dram);224		dev_warn(bpmp->dev, "failed to map DRAM region: %d\n", err);225		return err;226	}227 228	priv->rx.dram = priv->tx.dram + SZ_4K;229 230	return 0;231}232 233static int tegra186_bpmp_sram_init(struct tegra_bpmp *bpmp)234{235	struct tegra186_bpmp *priv = bpmp->priv;236	int err;237 238	priv->tx.pool = of_gen_pool_get(bpmp->dev->of_node, "shmem", 0);239	if (!priv->tx.pool) {240		dev_err(bpmp->dev, "TX shmem pool not found\n");241		return -EPROBE_DEFER;242	}243 244	priv->tx.sram = (void __iomem *)gen_pool_dma_alloc(priv->tx.pool, 4096,245							   &priv->tx.phys);246	if (!priv->tx.sram) {247		dev_err(bpmp->dev, "failed to allocate from TX pool\n");248		return -ENOMEM;249	}250 251	priv->rx.pool = of_gen_pool_get(bpmp->dev->of_node, "shmem", 1);252	if (!priv->rx.pool) {253		dev_err(bpmp->dev, "RX shmem pool not found\n");254		err = -EPROBE_DEFER;255		goto free_tx;256	}257 258	priv->rx.sram = (void __iomem *)gen_pool_dma_alloc(priv->rx.pool, 4096,259							   &priv->rx.phys);260	if (!priv->rx.sram) {261		dev_err(bpmp->dev, "failed to allocate from RX pool\n");262		err = -ENOMEM;263		goto free_tx;264	}265 266	return 0;267 268free_tx:269	gen_pool_free(priv->tx.pool, (unsigned long)priv->tx.sram, 4096);270 271	return err;272}273 274static int tegra186_bpmp_setup_channels(struct tegra_bpmp *bpmp)275{276	unsigned int i;277	int err;278 279	err = tegra186_bpmp_dram_init(bpmp);280	if (err == -ENODEV) {281		err = tegra186_bpmp_sram_init(bpmp);282		if (err < 0)283			return err;284	}285 286	err = tegra186_bpmp_channel_init(bpmp->tx_channel, bpmp,287					 bpmp->soc->channels.cpu_tx.offset);288	if (err < 0)289		return err;290 291	err = tegra186_bpmp_channel_init(bpmp->rx_channel, bpmp,292					 bpmp->soc->channels.cpu_rx.offset);293	if (err < 0) {294		tegra186_bpmp_channel_cleanup(bpmp->tx_channel);295		return err;296	}297 298	for (i = 0; i < bpmp->threaded.count; i++) {299		unsigned int index = bpmp->soc->channels.thread.offset + i;300 301		err = tegra186_bpmp_channel_init(&bpmp->threaded_channels[i],302						 bpmp, index);303		if (err < 0)304			break;305	}306 307	if (err < 0)308		tegra186_bpmp_teardown_channels(bpmp);309 310	return err;311}312 313static void tegra186_bpmp_reset_channels(struct tegra_bpmp *bpmp)314{315	unsigned int i;316 317	/* reset message channels */318	tegra186_bpmp_channel_reset(bpmp->tx_channel);319	tegra186_bpmp_channel_reset(bpmp->rx_channel);320 321	for (i = 0; i < bpmp->threaded.count; i++)322		tegra186_bpmp_channel_reset(&bpmp->threaded_channels[i]);323}324 325static int tegra186_bpmp_init(struct tegra_bpmp *bpmp)326{327	struct tegra186_bpmp *priv;328	int err;329 330	priv = devm_kzalloc(bpmp->dev, sizeof(*priv), GFP_KERNEL);331	if (!priv)332		return -ENOMEM;333 334	priv->parent = bpmp;335	bpmp->priv = priv;336 337	err = tegra186_bpmp_setup_channels(bpmp);338	if (err < 0)339		return err;340 341	/* mbox registration */342	priv->mbox.client.dev = bpmp->dev;343	priv->mbox.client.rx_callback = mbox_handle_rx;344	priv->mbox.client.tx_block = false;345	priv->mbox.client.knows_txdone = false;346 347	priv->mbox.channel = mbox_request_channel(&priv->mbox.client, 0);348	if (IS_ERR(priv->mbox.channel)) {349		err = PTR_ERR(priv->mbox.channel);350		dev_err(bpmp->dev, "failed to get HSP mailbox: %d\n", err);351		tegra186_bpmp_teardown_channels(bpmp);352		return err;353	}354 355	tegra186_bpmp_reset_channels(bpmp);356 357	return 0;358}359 360static void tegra186_bpmp_deinit(struct tegra_bpmp *bpmp)361{362	struct tegra186_bpmp *priv = bpmp->priv;363 364	mbox_free_channel(priv->mbox.channel);365 366	tegra186_bpmp_teardown_channels(bpmp);367}368 369static int tegra186_bpmp_resume(struct tegra_bpmp *bpmp)370{371	tegra186_bpmp_reset_channels(bpmp);372 373	return 0;374}375 376const struct tegra_bpmp_ops tegra186_bpmp_ops = {377	.init = tegra186_bpmp_init,378	.deinit = tegra186_bpmp_deinit,379	.is_response_ready = tegra186_bpmp_is_message_ready,380	.is_request_ready = tegra186_bpmp_is_message_ready,381	.ack_response = tegra186_bpmp_ack_message,382	.ack_request = tegra186_bpmp_ack_message,383	.is_response_channel_free = tegra186_bpmp_is_channel_free,384	.is_request_channel_free = tegra186_bpmp_is_channel_free,385	.post_response = tegra186_bpmp_post_message,386	.post_request = tegra186_bpmp_post_message,387	.ring_doorbell = tegra186_bpmp_ring_doorbell,388	.resume = tegra186_bpmp_resume,389};390