xref: /linux/drivers/bus/mhi/ep/ring.c (revision 06d07429858317ded2db7986113a9e0129cd599b)
1bbdcba57SManivannan Sadhasivam // SPDX-License-Identifier: GPL-2.0
2bbdcba57SManivannan Sadhasivam /*
3bbdcba57SManivannan Sadhasivam  * Copyright (C) 2022 Linaro Ltd.
4bbdcba57SManivannan Sadhasivam  * Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
5bbdcba57SManivannan Sadhasivam  */
6bbdcba57SManivannan Sadhasivam 
7bbdcba57SManivannan Sadhasivam #include <linux/mhi_ep.h>
8bbdcba57SManivannan Sadhasivam #include "internal.h"
9bbdcba57SManivannan Sadhasivam 
mhi_ep_ring_addr2offset(struct mhi_ep_ring * ring,u64 ptr)10bbdcba57SManivannan Sadhasivam size_t mhi_ep_ring_addr2offset(struct mhi_ep_ring *ring, u64 ptr)
11bbdcba57SManivannan Sadhasivam {
12bbdcba57SManivannan Sadhasivam 	return (ptr - ring->rbase) / sizeof(struct mhi_ring_element);
13bbdcba57SManivannan Sadhasivam }
14bbdcba57SManivannan Sadhasivam 
mhi_ep_ring_num_elems(struct mhi_ep_ring * ring)15bbdcba57SManivannan Sadhasivam static u32 mhi_ep_ring_num_elems(struct mhi_ep_ring *ring)
16bbdcba57SManivannan Sadhasivam {
17bbdcba57SManivannan Sadhasivam 	__le64 rlen;
18bbdcba57SManivannan Sadhasivam 
19bbdcba57SManivannan Sadhasivam 	memcpy_fromio(&rlen, (void __iomem *) &ring->ring_ctx->generic.rlen, sizeof(u64));
20bbdcba57SManivannan Sadhasivam 
21bbdcba57SManivannan Sadhasivam 	return le64_to_cpu(rlen) / sizeof(struct mhi_ring_element);
22bbdcba57SManivannan Sadhasivam }
23bbdcba57SManivannan Sadhasivam 
mhi_ep_ring_inc_index(struct mhi_ep_ring * ring)24bbdcba57SManivannan Sadhasivam void mhi_ep_ring_inc_index(struct mhi_ep_ring *ring)
25bbdcba57SManivannan Sadhasivam {
26bbdcba57SManivannan Sadhasivam 	ring->rd_offset = (ring->rd_offset + 1) % ring->ring_size;
27bbdcba57SManivannan Sadhasivam }
28bbdcba57SManivannan Sadhasivam 
__mhi_ep_cache_ring(struct mhi_ep_ring * ring,size_t end)29bbdcba57SManivannan Sadhasivam static int __mhi_ep_cache_ring(struct mhi_ep_ring *ring, size_t end)
30bbdcba57SManivannan Sadhasivam {
31bbdcba57SManivannan Sadhasivam 	struct mhi_ep_cntrl *mhi_cntrl = ring->mhi_cntrl;
32bbdcba57SManivannan Sadhasivam 	struct device *dev = &mhi_cntrl->mhi_dev->dev;
33b08ded2eSManivannan Sadhasivam 	struct mhi_ep_buf_info buf_info = {};
34b08ded2eSManivannan Sadhasivam 	size_t start;
35bbdcba57SManivannan Sadhasivam 	int ret;
36bbdcba57SManivannan Sadhasivam 
37bbdcba57SManivannan Sadhasivam 	/* Don't proceed in the case of event ring. This happens during mhi_ep_ring_start(). */
38bbdcba57SManivannan Sadhasivam 	if (ring->type == RING_TYPE_ER)
39bbdcba57SManivannan Sadhasivam 		return 0;
40bbdcba57SManivannan Sadhasivam 
41bbdcba57SManivannan Sadhasivam 	/* No need to cache the ring if write pointer is unmodified */
42bbdcba57SManivannan Sadhasivam 	if (ring->wr_offset == end)
43bbdcba57SManivannan Sadhasivam 		return 0;
44bbdcba57SManivannan Sadhasivam 
45bbdcba57SManivannan Sadhasivam 	start = ring->wr_offset;
46bbdcba57SManivannan Sadhasivam 	if (start < end) {
47b08ded2eSManivannan Sadhasivam 		buf_info.size = (end - start) * sizeof(struct mhi_ring_element);
48b08ded2eSManivannan Sadhasivam 		buf_info.host_addr = ring->rbase + (start * sizeof(struct mhi_ring_element));
49b08ded2eSManivannan Sadhasivam 		buf_info.dev_addr = &ring->ring_cache[start];
50b08ded2eSManivannan Sadhasivam 
51*92710524SManivannan Sadhasivam 		ret = mhi_cntrl->read_sync(mhi_cntrl, &buf_info);
52bbdcba57SManivannan Sadhasivam 		if (ret < 0)
53bbdcba57SManivannan Sadhasivam 			return ret;
54bbdcba57SManivannan Sadhasivam 	} else {
55b08ded2eSManivannan Sadhasivam 		buf_info.size = (ring->ring_size - start) * sizeof(struct mhi_ring_element);
56b08ded2eSManivannan Sadhasivam 		buf_info.host_addr = ring->rbase + (start * sizeof(struct mhi_ring_element));
57b08ded2eSManivannan Sadhasivam 		buf_info.dev_addr = &ring->ring_cache[start];
58b08ded2eSManivannan Sadhasivam 
59*92710524SManivannan Sadhasivam 		ret = mhi_cntrl->read_sync(mhi_cntrl, &buf_info);
60bbdcba57SManivannan Sadhasivam 		if (ret < 0)
61bbdcba57SManivannan Sadhasivam 			return ret;
62bbdcba57SManivannan Sadhasivam 
63bbdcba57SManivannan Sadhasivam 		if (end) {
64b08ded2eSManivannan Sadhasivam 			buf_info.host_addr = ring->rbase;
65b08ded2eSManivannan Sadhasivam 			buf_info.dev_addr = &ring->ring_cache[0];
66b08ded2eSManivannan Sadhasivam 			buf_info.size = end * sizeof(struct mhi_ring_element);
67b08ded2eSManivannan Sadhasivam 
68*92710524SManivannan Sadhasivam 			ret = mhi_cntrl->read_sync(mhi_cntrl, &buf_info);
69bbdcba57SManivannan Sadhasivam 			if (ret < 0)
70bbdcba57SManivannan Sadhasivam 				return ret;
71bbdcba57SManivannan Sadhasivam 		}
72bbdcba57SManivannan Sadhasivam 	}
73bbdcba57SManivannan Sadhasivam 
74b08ded2eSManivannan Sadhasivam 	dev_dbg(dev, "Cached ring: start %zu end %zu size %zu\n", start, end, buf_info.size);
75bbdcba57SManivannan Sadhasivam 
76bbdcba57SManivannan Sadhasivam 	return 0;
77bbdcba57SManivannan Sadhasivam }
78bbdcba57SManivannan Sadhasivam 
mhi_ep_cache_ring(struct mhi_ep_ring * ring,u64 wr_ptr)79bbdcba57SManivannan Sadhasivam static int mhi_ep_cache_ring(struct mhi_ep_ring *ring, u64 wr_ptr)
80bbdcba57SManivannan Sadhasivam {
81bbdcba57SManivannan Sadhasivam 	size_t wr_offset;
82bbdcba57SManivannan Sadhasivam 	int ret;
83bbdcba57SManivannan Sadhasivam 
84bbdcba57SManivannan Sadhasivam 	wr_offset = mhi_ep_ring_addr2offset(ring, wr_ptr);
85bbdcba57SManivannan Sadhasivam 
86bbdcba57SManivannan Sadhasivam 	/* Cache the host ring till write offset */
87bbdcba57SManivannan Sadhasivam 	ret = __mhi_ep_cache_ring(ring, wr_offset);
88bbdcba57SManivannan Sadhasivam 	if (ret)
89bbdcba57SManivannan Sadhasivam 		return ret;
90bbdcba57SManivannan Sadhasivam 
91bbdcba57SManivannan Sadhasivam 	ring->wr_offset = wr_offset;
92bbdcba57SManivannan Sadhasivam 
93bbdcba57SManivannan Sadhasivam 	return 0;
94bbdcba57SManivannan Sadhasivam }
95bbdcba57SManivannan Sadhasivam 
mhi_ep_update_wr_offset(struct mhi_ep_ring * ring)96bbdcba57SManivannan Sadhasivam int mhi_ep_update_wr_offset(struct mhi_ep_ring *ring)
97bbdcba57SManivannan Sadhasivam {
98bbdcba57SManivannan Sadhasivam 	u64 wr_ptr;
99bbdcba57SManivannan Sadhasivam 
100bbdcba57SManivannan Sadhasivam 	wr_ptr = mhi_ep_mmio_get_db(ring);
101bbdcba57SManivannan Sadhasivam 
102bbdcba57SManivannan Sadhasivam 	return mhi_ep_cache_ring(ring, wr_ptr);
103bbdcba57SManivannan Sadhasivam }
104bbdcba57SManivannan Sadhasivam 
105bbdcba57SManivannan Sadhasivam /* TODO: Support for adding multiple ring elements to the ring */
mhi_ep_ring_add_element(struct mhi_ep_ring * ring,struct mhi_ring_element * el)106bbdcba57SManivannan Sadhasivam int mhi_ep_ring_add_element(struct mhi_ep_ring *ring, struct mhi_ring_element *el)
107bbdcba57SManivannan Sadhasivam {
108bbdcba57SManivannan Sadhasivam 	struct mhi_ep_cntrl *mhi_cntrl = ring->mhi_cntrl;
109bbdcba57SManivannan Sadhasivam 	struct device *dev = &mhi_cntrl->mhi_dev->dev;
110b08ded2eSManivannan Sadhasivam 	struct mhi_ep_buf_info buf_info = {};
111bbdcba57SManivannan Sadhasivam 	size_t old_offset = 0;
112bbdcba57SManivannan Sadhasivam 	u32 num_free_elem;
113bbdcba57SManivannan Sadhasivam 	__le64 rp;
114bbdcba57SManivannan Sadhasivam 	int ret;
115bbdcba57SManivannan Sadhasivam 
116bbdcba57SManivannan Sadhasivam 	ret = mhi_ep_update_wr_offset(ring);
117bbdcba57SManivannan Sadhasivam 	if (ret) {
118bbdcba57SManivannan Sadhasivam 		dev_err(dev, "Error updating write pointer\n");
119bbdcba57SManivannan Sadhasivam 		return ret;
120bbdcba57SManivannan Sadhasivam 	}
121bbdcba57SManivannan Sadhasivam 
122bbdcba57SManivannan Sadhasivam 	if (ring->rd_offset < ring->wr_offset)
123bbdcba57SManivannan Sadhasivam 		num_free_elem = (ring->wr_offset - ring->rd_offset) - 1;
124bbdcba57SManivannan Sadhasivam 	else
125bbdcba57SManivannan Sadhasivam 		num_free_elem = ((ring->ring_size - ring->rd_offset) + ring->wr_offset) - 1;
126bbdcba57SManivannan Sadhasivam 
127bbdcba57SManivannan Sadhasivam 	/* Check if there is space in ring for adding at least an element */
128bbdcba57SManivannan Sadhasivam 	if (!num_free_elem) {
129bbdcba57SManivannan Sadhasivam 		dev_err(dev, "No space left in the ring\n");
130bbdcba57SManivannan Sadhasivam 		return -ENOSPC;
131bbdcba57SManivannan Sadhasivam 	}
132bbdcba57SManivannan Sadhasivam 
133bbdcba57SManivannan Sadhasivam 	old_offset = ring->rd_offset;
134bbdcba57SManivannan Sadhasivam 	mhi_ep_ring_inc_index(ring);
135bbdcba57SManivannan Sadhasivam 
136bbdcba57SManivannan Sadhasivam 	dev_dbg(dev, "Adding an element to ring at offset (%zu)\n", ring->rd_offset);
137bbdcba57SManivannan Sadhasivam 
138bbdcba57SManivannan Sadhasivam 	/* Update rp in ring context */
139bbdcba57SManivannan Sadhasivam 	rp = cpu_to_le64(ring->rd_offset * sizeof(*el) + ring->rbase);
140bbdcba57SManivannan Sadhasivam 	memcpy_toio((void __iomem *) &ring->ring_ctx->generic.rp, &rp, sizeof(u64));
141bbdcba57SManivannan Sadhasivam 
142b08ded2eSManivannan Sadhasivam 	buf_info.host_addr = ring->rbase + (old_offset * sizeof(*el));
143b08ded2eSManivannan Sadhasivam 	buf_info.dev_addr = el;
144b08ded2eSManivannan Sadhasivam 	buf_info.size = sizeof(*el);
145bbdcba57SManivannan Sadhasivam 
146*92710524SManivannan Sadhasivam 	return mhi_cntrl->write_sync(mhi_cntrl, &buf_info);
147bbdcba57SManivannan Sadhasivam }
148bbdcba57SManivannan Sadhasivam 
mhi_ep_ring_init(struct mhi_ep_ring * ring,enum mhi_ep_ring_type type,u32 id)149bbdcba57SManivannan Sadhasivam void mhi_ep_ring_init(struct mhi_ep_ring *ring, enum mhi_ep_ring_type type, u32 id)
150bbdcba57SManivannan Sadhasivam {
151bbdcba57SManivannan Sadhasivam 	ring->type = type;
152bbdcba57SManivannan Sadhasivam 	if (ring->type == RING_TYPE_CMD) {
153bbdcba57SManivannan Sadhasivam 		ring->db_offset_h = EP_CRDB_HIGHER;
154bbdcba57SManivannan Sadhasivam 		ring->db_offset_l = EP_CRDB_LOWER;
155bbdcba57SManivannan Sadhasivam 	} else if (ring->type == RING_TYPE_CH) {
156bbdcba57SManivannan Sadhasivam 		ring->db_offset_h = CHDB_HIGHER_n(id);
157bbdcba57SManivannan Sadhasivam 		ring->db_offset_l = CHDB_LOWER_n(id);
158bbdcba57SManivannan Sadhasivam 		ring->ch_id = id;
159bbdcba57SManivannan Sadhasivam 	} else {
160bbdcba57SManivannan Sadhasivam 		ring->db_offset_h = ERDB_HIGHER_n(id);
161bbdcba57SManivannan Sadhasivam 		ring->db_offset_l = ERDB_LOWER_n(id);
162bbdcba57SManivannan Sadhasivam 	}
163bbdcba57SManivannan Sadhasivam }
164bbdcba57SManivannan Sadhasivam 
mhi_ep_raise_irq(struct work_struct * work)165cea4bcbfSManivannan Sadhasivam static void mhi_ep_raise_irq(struct work_struct *work)
166cea4bcbfSManivannan Sadhasivam {
167cea4bcbfSManivannan Sadhasivam 	struct mhi_ep_ring *ring = container_of(work, struct mhi_ep_ring, intmodt_work.work);
168cea4bcbfSManivannan Sadhasivam 	struct mhi_ep_cntrl *mhi_cntrl = ring->mhi_cntrl;
169cea4bcbfSManivannan Sadhasivam 
170cea4bcbfSManivannan Sadhasivam 	mhi_cntrl->raise_irq(mhi_cntrl, ring->irq_vector);
171cea4bcbfSManivannan Sadhasivam 	WRITE_ONCE(ring->irq_pending, false);
172cea4bcbfSManivannan Sadhasivam }
173cea4bcbfSManivannan Sadhasivam 
mhi_ep_ring_start(struct mhi_ep_cntrl * mhi_cntrl,struct mhi_ep_ring * ring,union mhi_ep_ring_ctx * ctx)174bbdcba57SManivannan Sadhasivam int mhi_ep_ring_start(struct mhi_ep_cntrl *mhi_cntrl, struct mhi_ep_ring *ring,
175bbdcba57SManivannan Sadhasivam 			union mhi_ep_ring_ctx *ctx)
176bbdcba57SManivannan Sadhasivam {
177bbdcba57SManivannan Sadhasivam 	struct device *dev = &mhi_cntrl->mhi_dev->dev;
178bbdcba57SManivannan Sadhasivam 	__le64 val;
179bbdcba57SManivannan Sadhasivam 	int ret;
180bbdcba57SManivannan Sadhasivam 
181bbdcba57SManivannan Sadhasivam 	ring->mhi_cntrl = mhi_cntrl;
182bbdcba57SManivannan Sadhasivam 	ring->ring_ctx = ctx;
183bbdcba57SManivannan Sadhasivam 	ring->ring_size = mhi_ep_ring_num_elems(ring);
184bbdcba57SManivannan Sadhasivam 	memcpy_fromio(&val, (void __iomem *) &ring->ring_ctx->generic.rbase, sizeof(u64));
185bbdcba57SManivannan Sadhasivam 	ring->rbase = le64_to_cpu(val);
186bbdcba57SManivannan Sadhasivam 
187bbdcba57SManivannan Sadhasivam 	if (ring->type == RING_TYPE_CH)
188bbdcba57SManivannan Sadhasivam 		ring->er_index = le32_to_cpu(ring->ring_ctx->ch.erindex);
189bbdcba57SManivannan Sadhasivam 
190cea4bcbfSManivannan Sadhasivam 	if (ring->type == RING_TYPE_ER) {
191bbdcba57SManivannan Sadhasivam 		ring->irq_vector = le32_to_cpu(ring->ring_ctx->ev.msivec);
192cea4bcbfSManivannan Sadhasivam 		ring->intmodt = FIELD_GET(EV_CTX_INTMODT_MASK,
193cea4bcbfSManivannan Sadhasivam 					  le32_to_cpu(ring->ring_ctx->ev.intmod));
194cea4bcbfSManivannan Sadhasivam 
195cea4bcbfSManivannan Sadhasivam 		INIT_DELAYED_WORK(&ring->intmodt_work, mhi_ep_raise_irq);
196cea4bcbfSManivannan Sadhasivam 	}
197bbdcba57SManivannan Sadhasivam 
198bbdcba57SManivannan Sadhasivam 	/* During ring init, both rp and wp are equal */
199bbdcba57SManivannan Sadhasivam 	memcpy_fromio(&val, (void __iomem *) &ring->ring_ctx->generic.rp, sizeof(u64));
200bbdcba57SManivannan Sadhasivam 	ring->rd_offset = mhi_ep_ring_addr2offset(ring, le64_to_cpu(val));
201bbdcba57SManivannan Sadhasivam 	ring->wr_offset = mhi_ep_ring_addr2offset(ring, le64_to_cpu(val));
202bbdcba57SManivannan Sadhasivam 
203bbdcba57SManivannan Sadhasivam 	/* Allocate ring cache memory for holding the copy of host ring */
204bbdcba57SManivannan Sadhasivam 	ring->ring_cache = kcalloc(ring->ring_size, sizeof(struct mhi_ring_element), GFP_KERNEL);
205bbdcba57SManivannan Sadhasivam 	if (!ring->ring_cache)
206bbdcba57SManivannan Sadhasivam 		return -ENOMEM;
207bbdcba57SManivannan Sadhasivam 
208bbdcba57SManivannan Sadhasivam 	memcpy_fromio(&val, (void __iomem *) &ring->ring_ctx->generic.wp, sizeof(u64));
209bbdcba57SManivannan Sadhasivam 	ret = mhi_ep_cache_ring(ring, le64_to_cpu(val));
210bbdcba57SManivannan Sadhasivam 	if (ret) {
211bbdcba57SManivannan Sadhasivam 		dev_err(dev, "Failed to cache ring\n");
212bbdcba57SManivannan Sadhasivam 		kfree(ring->ring_cache);
213bbdcba57SManivannan Sadhasivam 		return ret;
214bbdcba57SManivannan Sadhasivam 	}
215bbdcba57SManivannan Sadhasivam 
216bbdcba57SManivannan Sadhasivam 	ring->started = true;
217bbdcba57SManivannan Sadhasivam 
218bbdcba57SManivannan Sadhasivam 	return 0;
219bbdcba57SManivannan Sadhasivam }
220bbdcba57SManivannan Sadhasivam 
mhi_ep_ring_reset(struct mhi_ep_cntrl * mhi_cntrl,struct mhi_ep_ring * ring)221bbdcba57SManivannan Sadhasivam void mhi_ep_ring_reset(struct mhi_ep_cntrl *mhi_cntrl, struct mhi_ep_ring *ring)
222bbdcba57SManivannan Sadhasivam {
223cea4bcbfSManivannan Sadhasivam 	if (ring->type == RING_TYPE_ER)
224cea4bcbfSManivannan Sadhasivam 		cancel_delayed_work_sync(&ring->intmodt_work);
225cea4bcbfSManivannan Sadhasivam 
226bbdcba57SManivannan Sadhasivam 	ring->started = false;
227bbdcba57SManivannan Sadhasivam 	kfree(ring->ring_cache);
228bbdcba57SManivannan Sadhasivam 	ring->ring_cache = NULL;
229bbdcba57SManivannan Sadhasivam }
230