Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ab5d6318 authored by Jeff Hugo's avatar Jeff Hugo
Browse files

soc: qcom: glink: Add mailbox transport



The mailbox transport enables Glink communication over hardware mailboxes
that manage the fifo location.

Change-Id: I9bb7520e09e79843c7ec729bed2e75b77c7c047e
Signed-off-by: default avatarJeffrey Hugo <jhugo@codeaurora.org>
parent 50e0173f
Loading
Loading
Loading
Loading
+35 −0
Original line number Diff line number Diff line
Qualcomm Technologies, Inc. G-link Mailbox Transport

Required properties:
-compatible : should be "qcom,glink-mailbox-xprt"
-reg : the mailbox register to store the location of the fifo
	the mailbox register to store the size of the fifo
	the irq register base address for triggering interrupts
	the register to enable sending interrupts
	the register to reset the rx irq line
-reg-names : "mbox-loc-addr" - string to identify the mailbox location reg
		"mbox-loc-size" - string to identify the mailbox size reg
		"irq-reg-base" - string to identify the irq register region
		"irq-rx-reset" - string to identify the rx irq reset register
-qcom,irq-mask : the bitmask to trigger an interrupt
-interrupts : the receiving interrupt line
-label : the name of the subsystem this link connects to
-qcom,tx-ring-size: size of the transmit ring buffer in bytes
-qcom,rx-ring-size: size of the receive ring buffer in bytes

Example:

	qcom,glink-mailbox-xprt-spss@1d05008 {
		compatible = "qcom,glink-mailbox-xprt";
		reg = <0x1d05008 0x8>,
			<0x1d05010 0x4>,
			<0x1d06004 0x4>,
			<0x1d06020 0x4>;
		reg-names = "mbox-loc-addr", "mbox-loc-size", "irq-reg-base",
			"irq-rx-reset";
		qcom,irq-mask = <0x1000>;
		interrupts = <0 25 4>;
		label = "spss";
		qcom,tx-ring-size = <0x400>;
		qcom,rx-ring-size = <0x400>;
	};
+4 −0
Original line number Diff line number Diff line
@@ -3340,6 +3340,10 @@ int glink_xprt_name_to_id(const char *name, uint16_t *id)
		*id = SMEM_XPRT_ID;
		return 0;
	}
	if (!strcmp(name, "mailbox")) {
		*id = SMEM_XPRT_ID;
		return 0;
	}
	if (!strcmp(name, "smd_trans")) {
		*id = SMD_TRANS_XPRT_ID;
		return 0;
+328 −5
Original line number Diff line number Diff line
@@ -110,6 +110,26 @@ struct channel_desc {
	uint32_t write_index;
};

/**
 * struct mailbox_config_info - description of a mailbox tranposrt channel
 * @tx_read_index:	Offset into the tx fifo where data should be read from.
 * @tx_write_index:	Offset into the tx fifo where new data will be placed.
 * @tx_size:		Size of the transmit fifo in bytes.
 * @rx_read_index:	Offset into the rx fifo where data should be read from.
 * @rx_write_index:	Offset into the rx fifo where new data will be placed.
 * @rx_size:		Size of the receive fifo in bytes.
 * @fifo:		The fifos for the channel.
 */
struct mailbox_config_info {
	uint32_t tx_read_index;
	uint32_t tx_write_index;
	uint32_t tx_size;
	uint32_t rx_read_index;
	uint32_t rx_write_index;
	uint32_t rx_size;
	char fifo[]; /* tx fifo, then rx fifo */
};

/**
 * struct edge_info - local information for managing a single complete edge
 * @xprt_if:			The transport interface registered with the
@@ -120,6 +140,8 @@ struct channel_desc {
 * @irq_disabled:		Flag indicating the whether interrupt is enabled
 *				or disabled.
 * @remote_proc_id:		The SMEM processor id for the remote side.
 * @rx_reset_reg:		Reference to the register to reset the rx irq
 *				line, if applicable.
 * @out_irq_reg:		Reference to the register to send an irq to the
 *				remote side.
 * @out_irq_mask:		Mask written to @out_irq_reg to trigger the
@@ -159,6 +181,7 @@ struct channel_desc {
 * @num_pw_states:		Size of @ramp_time_us.
 * @ramp_time_us:		Array of ramp times in microseconds where array
 *				index position represents a power state.
 * @mailbox:			Mailbox transport channel description reference.
 */
struct edge_info {
	struct glink_transport_if xprt_if;
@@ -166,6 +189,7 @@ struct edge_info {
	bool intentless;
	bool irq_disabled;
	uint32_t remote_proc_id;
	void __iomem *rx_reset_reg;
	void __iomem *out_irq_reg;
	uint32_t out_irq_mask;
	uint32_t irq_line;
@@ -192,6 +216,7 @@ struct edge_info {
	struct list_head deferred_cmds;
	uint32_t num_pw_states;
	unsigned long *ramp_time_us;
	struct mailbox_config_info *mailbox;
};

/**
@@ -743,6 +768,29 @@ static bool queue_cmd(struct edge_info *einfo, void *cmd, void *data)
	return true;
}

/**
 * get_rx_fifo() - Find the rx fifo for an edge
 * @einfo:	Edge to find the fifo for.
 *
 * Return: True if fifo was found, false otherwise.
 */
static bool get_rx_fifo(struct edge_info *einfo)
{
	if (einfo->mailbox) {
		einfo->rx_fifo = &einfo->mailbox->fifo[einfo->mailbox->tx_size];
		einfo->rx_fifo_size = einfo->mailbox->rx_size;
	} else {
		einfo->rx_fifo = smem_get_entry(SMEM_GLINK_NATIVE_XPRT_FIFO_1,
							&einfo->rx_fifo_size,
							einfo->remote_proc_id,
							SMEM_ITEM_CACHED_FLAG);
		if (!einfo->rx_fifo)
			return false;
	}

	return true;
}

/**
 * __rx_worker() - process received commands on a specific edge
 * @einfo:	Edge to process commands on.
@@ -779,11 +827,7 @@ static void __rx_worker(struct edge_info *einfo, bool atomic_ctx)
	rcu_id = srcu_read_lock(&einfo->use_ref);

	if (unlikely(!einfo->rx_fifo)) {
		einfo->rx_fifo = smem_get_entry(SMEM_GLINK_NATIVE_XPRT_FIFO_1,
							&einfo->rx_fifo_size,
							einfo->remote_proc_id,
							SMEM_ITEM_CACHED_FLAG);
		if (!einfo->rx_fifo) {
		if (!get_rx_fifo(einfo)) {
			srcu_read_unlock(&einfo->use_ref, rcu_id);
			return;
		}
@@ -1116,6 +1160,9 @@ irqreturn_t irq_handler(int irq, void *priv)
{
	struct edge_info *einfo = (struct edge_info *)priv;

	if (einfo->rx_reset_reg)
		writel_relaxed(einfo->out_irq_mask, einfo->rx_reset_reg);

	queue_kthread_work(&einfo->kworker, &einfo->kwork);
	einfo->rx_irq_count++;

@@ -2126,6 +2173,8 @@ static int subsys_name_to_id(const char *name)
		return SMEM_RPM;
	if (!strcmp(name, "wcnss"))
		return SMEM_WCNSS;
	if (!strcmp(name, "spss"))
		return SMEM_SPSS;
	return -ENODEV;
}

@@ -2556,6 +2605,259 @@ edge_info_alloc_fail:
	return rc;
}

static int glink_mailbox_probe(struct platform_device *pdev)
{
	struct device_node *node;
	struct edge_info *einfo;
	int rc;
	char *key;
	const char *subsys_name;
	uint32_t irq_line;
	uint32_t irq_mask;
	struct resource *irq_r;
	struct resource *mbox_loc_r;
	struct resource *mbox_size_r;
	struct resource *rx_reset_r;
	void *mbox_loc;
	void *mbox_size;
	struct mailbox_config_info *mbox_cfg;
	uint32_t mbox_cfg_size;
	phys_addr_t cfg_p_addr;

	node = pdev->dev.of_node;

	einfo = kzalloc(sizeof(*einfo), GFP_KERNEL);
	if (!einfo) {
		rc = -ENOMEM;
		goto edge_info_alloc_fail;
	}

	key = "label";
	subsys_name = of_get_property(node, key, NULL);
	if (!subsys_name) {
		pr_err("%s: missing key %s\n", __func__, key);
		rc = -ENODEV;
		goto missing_key;
	}

	key = "interrupts";
	irq_line = irq_of_parse_and_map(node, 0);
	if (!irq_line) {
		pr_err("%s: missing key %s\n", __func__, key);
		rc = -ENODEV;
		goto missing_key;
	}

	key = "qcom,irq-mask";
	rc = of_property_read_u32(node, key, &irq_mask);
	if (rc) {
		pr_err("%s: missing key %s\n", __func__, key);
		rc = -ENODEV;
		goto missing_key;
	}

	key = "irq-reg-base";
	irq_r = platform_get_resource_byname(pdev, IORESOURCE_MEM, key);
	if (!irq_r) {
		pr_err("%s: missing key %s\n", __func__, key);
		rc = -ENODEV;
		goto missing_key;
	}

	key = "mbox-loc-addr";
	mbox_loc_r = platform_get_resource_byname(pdev, IORESOURCE_MEM, key);
	if (!mbox_loc_r) {
		pr_err("%s: missing key %s\n", __func__, key);
		rc = -ENODEV;
		goto missing_key;
	}

	key = "mbox-loc-size";
	mbox_size_r = platform_get_resource_byname(pdev, IORESOURCE_MEM, key);
	if (!mbox_size_r) {
		pr_err("%s: missing key %s\n", __func__, key);
		rc = -ENODEV;
		goto missing_key;
	}

	key = "irq-rx-reset";
	rx_reset_r = platform_get_resource_byname(pdev, IORESOURCE_MEM, key);
	if (!irq_r) {
		pr_err("%s: missing key %s\n", __func__, key);
		rc = -ENODEV;
		goto missing_key;
	}

	key = "qcom,tx-ring-size";
	rc = of_property_read_u32(node, key, &einfo->tx_fifo_size);
	if (rc) {
		pr_err("%s: missing key %s\n", __func__, key);
		rc = -ENODEV;
		goto missing_key;
	}

	key = "qcom,rx-ring-size";
	rc = of_property_read_u32(node, key, &einfo->rx_fifo_size);
	if (rc) {
		pr_err("%s: missing key %s\n", __func__, key);
		rc = -ENODEV;
		goto missing_key;
	}

	if (subsys_name_to_id(subsys_name) == -ENODEV) {
		pr_err("%s: unknown subsystem: %s\n", __func__, subsys_name);
		rc = -ENODEV;
		goto invalid_key;
	}
	einfo->remote_proc_id = subsys_name_to_id(subsys_name);

	init_xprt_cfg(einfo, subsys_name);
	einfo->xprt_cfg.name = "mailbox";
	init_xprt_if(einfo);
	spin_lock_init(&einfo->write_lock);
	init_waitqueue_head(&einfo->tx_blocked_queue);
	init_kthread_work(&einfo->kwork, rx_worker);
	init_kthread_worker(&einfo->kworker);
	einfo->read_from_fifo = read_from_fifo;
	einfo->write_to_fifo = write_to_fifo;
	init_srcu_struct(&einfo->use_ref);
	spin_lock_init(&einfo->rx_lock);
	INIT_LIST_HEAD(&einfo->deferred_cmds);

	mutex_lock(&probe_lock);
	if (edge_infos[einfo->remote_proc_id]) {
		pr_err("%s: duplicate subsys %s is not valid\n", __func__,
								subsys_name);
		rc = -ENODEV;
		mutex_unlock(&probe_lock);
		goto invalid_key;
	}
	edge_infos[einfo->remote_proc_id] = einfo;
	mutex_unlock(&probe_lock);

	einfo->out_irq_mask = irq_mask;
	einfo->out_irq_reg = ioremap_nocache(irq_r->start,
							resource_size(irq_r));
	if (!einfo->out_irq_reg) {
		pr_err("%s: unable to map irq reg\n", __func__);
		rc = -ENOMEM;
		goto irq_ioremap_fail;
	}

	mbox_loc = ioremap_nocache(mbox_loc_r->start,
						resource_size(mbox_loc_r));
	if (!mbox_loc) {
		pr_err("%s: unable to map mailbox location reg\n", __func__);
		rc = -ENOMEM;
		goto mbox_loc_ioremap_fail;
	}

	mbox_size = ioremap_nocache(mbox_size_r->start,
						resource_size(mbox_size_r));
	if (!mbox_size) {
		pr_err("%s: unable to map mailbox size reg\n", __func__);
		rc = -ENOMEM;
		goto mbox_size_ioremap_fail;
	}

	einfo->rx_reset_reg = ioremap_nocache(rx_reset_r->start,
						resource_size(rx_reset_r));
	if (!einfo->rx_reset_reg) {
		pr_err("%s: unable to map rx reset reg\n", __func__);
		rc = -ENOMEM;
		goto rx_reset_ioremap_fail;
	}

	einfo->task = kthread_run(kthread_worker_fn, &einfo->kworker,
						"smem_native_%s", subsys_name);
	if (IS_ERR(einfo->task)) {
		rc = PTR_ERR(einfo->task);
		pr_err("%s: kthread_run failed %d\n", __func__, rc);
		goto kthread_fail;
	}

	mbox_cfg_size = sizeof(*mbox_cfg) + einfo->tx_fifo_size +
							einfo->rx_fifo_size;
	mbox_cfg = smem_alloc(SMEM_GLINK_NATIVE_XPRT_DESCRIPTOR,
							mbox_cfg_size,
							einfo->remote_proc_id,
							0);
	if (PTR_ERR(mbox_cfg) == -EPROBE_DEFER) {
		rc = -EPROBE_DEFER;
		goto smem_alloc_fail;
	}
	if (!mbox_cfg) {
		pr_err("%s: smem alloc of mailbox struct failed\n", __func__);
		rc = -ENOMEM;
		goto smem_alloc_fail;
	}
	einfo->mailbox = mbox_cfg;
	einfo->tx_ch_desc = (struct channel_desc *)(&mbox_cfg->tx_read_index);
	einfo->rx_ch_desc = (struct channel_desc *)(&mbox_cfg->rx_read_index);
	mbox_cfg->tx_size = einfo->tx_fifo_size;
	mbox_cfg->rx_size = einfo->rx_fifo_size;
	einfo->tx_fifo = &mbox_cfg->fifo[0];

	rc = glink_core_register_transport(&einfo->xprt_if, &einfo->xprt_cfg);
	if (rc == -EPROBE_DEFER)
		goto reg_xprt_fail;
	if (rc) {
		pr_err("%s: glink core register transport failed: %d\n",
								__func__, rc);
		goto reg_xprt_fail;
	}

	einfo->irq_line = irq_line;
	rc = request_irq(irq_line, irq_handler,
			IRQF_TRIGGER_HIGH | IRQF_NO_SUSPEND | IRQF_SHARED,
			node->name, einfo);
	if (rc < 0) {
		pr_err("%s: request_irq on %d failed: %d\n", __func__, irq_line,
									rc);
		goto request_irq_fail;
	}
	rc = enable_irq_wake(irq_line);
	if (rc < 0)
		pr_err("%s: enable_irq_wake() failed on %d\n", __func__,
								irq_line);

	register_debugfs_info(einfo);

	writel_relaxed(mbox_cfg_size, mbox_size);
	cfg_p_addr = smem_virt_to_phys(mbox_cfg);
	writel_relaxed((uint32_t)cfg_p_addr, mbox_loc);
	writel_relaxed((uint32_t)(((uint64_t)cfg_p_addr)>>32), mbox_loc + 4);
	send_irq(einfo);
	iounmap(mbox_size);
	iounmap(mbox_loc);
	return 0;

request_irq_fail:
	glink_core_unregister_transport(&einfo->xprt_if);
reg_xprt_fail:
smem_alloc_fail:
	flush_kthread_worker(&einfo->kworker);
	kthread_stop(einfo->task);
	einfo->task = NULL;
kthread_fail:
	iounmap(einfo->rx_reset_reg);
rx_reset_ioremap_fail:
	iounmap(mbox_size);
mbox_size_ioremap_fail:
	iounmap(mbox_loc);
mbox_loc_ioremap_fail:
	iounmap(einfo->out_irq_reg);
irq_ioremap_fail:
	mutex_lock(&probe_lock);
	edge_infos[einfo->remote_proc_id] = NULL;
	mutex_unlock(&probe_lock);
invalid_key:
missing_key:
	kfree(einfo);
edge_info_alloc_fail:
	return rc;
}

#if defined(CONFIG_DEBUG_FS)
/**
 * debug_edge() - generates formatted text output displaying current edge state
@@ -2679,6 +2981,20 @@ static struct platform_driver glink_rpm_native_driver = {
	},
};

static struct of_device_id mailbox_match_table[] = {
	{ .compatible = "qcom,glink-mailbox-xprt" },
	{},
};

static struct platform_driver glink_mailbox_driver = {
	.probe = glink_mailbox_probe,
	.driver = {
		.name = "msm_glink_mailbox_xprt",
		.owner = THIS_MODULE,
		.of_match_table = mailbox_match_table,
	},
};

static int __init glink_smem_native_xprt_init(void)
{
	int rc;
@@ -2697,6 +3013,13 @@ static int __init glink_smem_native_xprt_init(void)
		return rc;
	}

	rc = platform_driver_register(&glink_mailbox_driver);
	if (rc) {
		pr_err("%s: glink_mailbox_driver register failed %d\n",
								__func__, rc);
		return rc;
	}

	return 0;
}
arch_initcall(glink_smem_native_xprt_init);