xref: /OK3568_Linux_fs/kernel/drivers/media/pci/saa7164/saa7164-bus.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  *  Driver for the NXP SAA7164 PCIe bridge
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  *  Copyright (c) 2010-2015 Steven Toth <stoth@kernellabs.com>
6*4882a593Smuzhiyun  */
7*4882a593Smuzhiyun 
8*4882a593Smuzhiyun #include "saa7164.h"
9*4882a593Smuzhiyun 
10*4882a593Smuzhiyun /* The message bus to/from the firmware is a ring buffer in PCI address
11*4882a593Smuzhiyun  * space. Establish the defaults.
12*4882a593Smuzhiyun  */
saa7164_bus_setup(struct saa7164_dev * dev)13*4882a593Smuzhiyun int saa7164_bus_setup(struct saa7164_dev *dev)
14*4882a593Smuzhiyun {
15*4882a593Smuzhiyun 	struct tmComResBusInfo *b	= &dev->bus;
16*4882a593Smuzhiyun 
17*4882a593Smuzhiyun 	mutex_init(&b->lock);
18*4882a593Smuzhiyun 
19*4882a593Smuzhiyun 	b->Type			= TYPE_BUS_PCIe;
20*4882a593Smuzhiyun 	b->m_wMaxReqSize	= SAA_DEVICE_MAXREQUESTSIZE;
21*4882a593Smuzhiyun 
22*4882a593Smuzhiyun 	b->m_pdwSetRing		= (u8 __iomem *)(dev->bmmio +
23*4882a593Smuzhiyun 		((u32)dev->busdesc.CommandRing));
24*4882a593Smuzhiyun 
25*4882a593Smuzhiyun 	b->m_dwSizeSetRing	= SAA_DEVICE_BUFFERBLOCKSIZE;
26*4882a593Smuzhiyun 
27*4882a593Smuzhiyun 	b->m_pdwGetRing		= (u8 __iomem *)(dev->bmmio +
28*4882a593Smuzhiyun 		((u32)dev->busdesc.ResponseRing));
29*4882a593Smuzhiyun 
30*4882a593Smuzhiyun 	b->m_dwSizeGetRing	= SAA_DEVICE_BUFFERBLOCKSIZE;
31*4882a593Smuzhiyun 
32*4882a593Smuzhiyun 	b->m_dwSetWritePos	= ((u32)dev->intfdesc.BARLocation) +
33*4882a593Smuzhiyun 		(2 * sizeof(u64));
34*4882a593Smuzhiyun 	b->m_dwSetReadPos	= b->m_dwSetWritePos + (1 * sizeof(u32));
35*4882a593Smuzhiyun 
36*4882a593Smuzhiyun 	b->m_dwGetWritePos	= b->m_dwSetWritePos + (2 * sizeof(u32));
37*4882a593Smuzhiyun 	b->m_dwGetReadPos	= b->m_dwSetWritePos + (3 * sizeof(u32));
38*4882a593Smuzhiyun 
39*4882a593Smuzhiyun 	return 0;
40*4882a593Smuzhiyun }
41*4882a593Smuzhiyun 
saa7164_bus_dump(struct saa7164_dev * dev)42*4882a593Smuzhiyun void saa7164_bus_dump(struct saa7164_dev *dev)
43*4882a593Smuzhiyun {
44*4882a593Smuzhiyun 	struct tmComResBusInfo *b = &dev->bus;
45*4882a593Smuzhiyun 
46*4882a593Smuzhiyun 	dprintk(DBGLVL_BUS, "Dumping the bus structure:\n");
47*4882a593Smuzhiyun 	dprintk(DBGLVL_BUS, " .type             = %d\n", b->Type);
48*4882a593Smuzhiyun 	dprintk(DBGLVL_BUS, " .dev->bmmio       = 0x%p\n", dev->bmmio);
49*4882a593Smuzhiyun 	dprintk(DBGLVL_BUS, " .m_wMaxReqSize    = 0x%x\n", b->m_wMaxReqSize);
50*4882a593Smuzhiyun 	dprintk(DBGLVL_BUS, " .m_pdwSetRing     = 0x%p\n", b->m_pdwSetRing);
51*4882a593Smuzhiyun 	dprintk(DBGLVL_BUS, " .m_dwSizeSetRing  = 0x%x\n", b->m_dwSizeSetRing);
52*4882a593Smuzhiyun 	dprintk(DBGLVL_BUS, " .m_pdwGetRing     = 0x%p\n", b->m_pdwGetRing);
53*4882a593Smuzhiyun 	dprintk(DBGLVL_BUS, " .m_dwSizeGetRing  = 0x%x\n", b->m_dwSizeGetRing);
54*4882a593Smuzhiyun 
55*4882a593Smuzhiyun 	dprintk(DBGLVL_BUS, " .m_dwSetReadPos   = 0x%x (0x%08x)\n",
56*4882a593Smuzhiyun 		b->m_dwSetReadPos, saa7164_readl(b->m_dwSetReadPos));
57*4882a593Smuzhiyun 
58*4882a593Smuzhiyun 	dprintk(DBGLVL_BUS, " .m_dwSetWritePos  = 0x%x (0x%08x)\n",
59*4882a593Smuzhiyun 		b->m_dwSetWritePos, saa7164_readl(b->m_dwSetWritePos));
60*4882a593Smuzhiyun 
61*4882a593Smuzhiyun 	dprintk(DBGLVL_BUS, " .m_dwGetReadPos   = 0x%x (0x%08x)\n",
62*4882a593Smuzhiyun 		b->m_dwGetReadPos, saa7164_readl(b->m_dwGetReadPos));
63*4882a593Smuzhiyun 
64*4882a593Smuzhiyun 	dprintk(DBGLVL_BUS, " .m_dwGetWritePos  = 0x%x (0x%08x)\n",
65*4882a593Smuzhiyun 		b->m_dwGetWritePos, saa7164_readl(b->m_dwGetWritePos));
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun }
68*4882a593Smuzhiyun 
69*4882a593Smuzhiyun /* Intensionally throw a BUG() if the state of the message bus looks corrupt */
saa7164_bus_verify(struct saa7164_dev * dev)70*4882a593Smuzhiyun static void saa7164_bus_verify(struct saa7164_dev *dev)
71*4882a593Smuzhiyun {
72*4882a593Smuzhiyun 	struct tmComResBusInfo *b = &dev->bus;
73*4882a593Smuzhiyun 	int bug = 0;
74*4882a593Smuzhiyun 
75*4882a593Smuzhiyun 	if (saa7164_readl(b->m_dwSetReadPos) > b->m_dwSizeSetRing)
76*4882a593Smuzhiyun 		bug++;
77*4882a593Smuzhiyun 
78*4882a593Smuzhiyun 	if (saa7164_readl(b->m_dwSetWritePos) > b->m_dwSizeSetRing)
79*4882a593Smuzhiyun 		bug++;
80*4882a593Smuzhiyun 
81*4882a593Smuzhiyun 	if (saa7164_readl(b->m_dwGetReadPos) > b->m_dwSizeGetRing)
82*4882a593Smuzhiyun 		bug++;
83*4882a593Smuzhiyun 
84*4882a593Smuzhiyun 	if (saa7164_readl(b->m_dwGetWritePos) > b->m_dwSizeGetRing)
85*4882a593Smuzhiyun 		bug++;
86*4882a593Smuzhiyun 
87*4882a593Smuzhiyun 	if (bug) {
88*4882a593Smuzhiyun 		saa_debug = 0xffff; /* Ensure we get the bus dump */
89*4882a593Smuzhiyun 		saa7164_bus_dump(dev);
90*4882a593Smuzhiyun 		saa_debug = 1024; /* Ensure we get the bus dump */
91*4882a593Smuzhiyun 		BUG();
92*4882a593Smuzhiyun 	}
93*4882a593Smuzhiyun }
94*4882a593Smuzhiyun 
saa7164_bus_dumpmsg(struct saa7164_dev * dev,struct tmComResInfo * m,void * buf)95*4882a593Smuzhiyun static void saa7164_bus_dumpmsg(struct saa7164_dev *dev, struct tmComResInfo *m,
96*4882a593Smuzhiyun 				void *buf)
97*4882a593Smuzhiyun {
98*4882a593Smuzhiyun 	dprintk(DBGLVL_BUS, "Dumping msg structure:\n");
99*4882a593Smuzhiyun 	dprintk(DBGLVL_BUS, " .id               = %d\n",   m->id);
100*4882a593Smuzhiyun 	dprintk(DBGLVL_BUS, " .flags            = 0x%x\n", m->flags);
101*4882a593Smuzhiyun 	dprintk(DBGLVL_BUS, " .size             = 0x%x\n", m->size);
102*4882a593Smuzhiyun 	dprintk(DBGLVL_BUS, " .command          = 0x%x\n", m->command);
103*4882a593Smuzhiyun 	dprintk(DBGLVL_BUS, " .controlselector  = 0x%x\n", m->controlselector);
104*4882a593Smuzhiyun 	dprintk(DBGLVL_BUS, " .seqno            = %d\n",   m->seqno);
105*4882a593Smuzhiyun 	if (buf)
106*4882a593Smuzhiyun 		dprintk(DBGLVL_BUS, " .buffer (ignored)\n");
107*4882a593Smuzhiyun }
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun /*
110*4882a593Smuzhiyun  * Places a command or a response on the bus. The implementation does not
111*4882a593Smuzhiyun  * know if it is a command or a response it just places the data on the
112*4882a593Smuzhiyun  * bus depending on the bus information given in the struct tmComResBusInfo
113*4882a593Smuzhiyun  * structure. If the command or response does not fit into the bus ring
114*4882a593Smuzhiyun  * buffer it will be refused.
115*4882a593Smuzhiyun  *
116*4882a593Smuzhiyun  * Return Value:
117*4882a593Smuzhiyun  *  SAA_OK     The function executed successfully.
118*4882a593Smuzhiyun  *  < 0        One or more members are not initialized.
119*4882a593Smuzhiyun  */
saa7164_bus_set(struct saa7164_dev * dev,struct tmComResInfo * msg,void * buf)120*4882a593Smuzhiyun int saa7164_bus_set(struct saa7164_dev *dev, struct tmComResInfo* msg,
121*4882a593Smuzhiyun 	void *buf)
122*4882a593Smuzhiyun {
123*4882a593Smuzhiyun 	struct tmComResBusInfo *bus = &dev->bus;
124*4882a593Smuzhiyun 	u32 bytes_to_write, free_write_space, timeout, curr_srp, curr_swp;
125*4882a593Smuzhiyun 	u32 new_swp, space_rem;
126*4882a593Smuzhiyun 	int ret = SAA_ERR_BAD_PARAMETER;
127*4882a593Smuzhiyun 	u16 size;
128*4882a593Smuzhiyun 
129*4882a593Smuzhiyun 	if (!msg) {
130*4882a593Smuzhiyun 		printk(KERN_ERR "%s() !msg\n", __func__);
131*4882a593Smuzhiyun 		return SAA_ERR_BAD_PARAMETER;
132*4882a593Smuzhiyun 	}
133*4882a593Smuzhiyun 
134*4882a593Smuzhiyun 	dprintk(DBGLVL_BUS, "%s()\n", __func__);
135*4882a593Smuzhiyun 
136*4882a593Smuzhiyun 	saa7164_bus_verify(dev);
137*4882a593Smuzhiyun 
138*4882a593Smuzhiyun 	if (msg->size > dev->bus.m_wMaxReqSize) {
139*4882a593Smuzhiyun 		printk(KERN_ERR "%s() Exceeded dev->bus.m_wMaxReqSize\n",
140*4882a593Smuzhiyun 			__func__);
141*4882a593Smuzhiyun 		return SAA_ERR_BAD_PARAMETER;
142*4882a593Smuzhiyun 	}
143*4882a593Smuzhiyun 
144*4882a593Smuzhiyun 	if ((msg->size > 0) && (buf == NULL)) {
145*4882a593Smuzhiyun 		printk(KERN_ERR "%s() Missing message buffer\n", __func__);
146*4882a593Smuzhiyun 		return SAA_ERR_BAD_PARAMETER;
147*4882a593Smuzhiyun 	}
148*4882a593Smuzhiyun 
149*4882a593Smuzhiyun 	/* Lock the bus from any other access */
150*4882a593Smuzhiyun 	mutex_lock(&bus->lock);
151*4882a593Smuzhiyun 
152*4882a593Smuzhiyun 	bytes_to_write = sizeof(*msg) + msg->size;
153*4882a593Smuzhiyun 	free_write_space = 0;
154*4882a593Smuzhiyun 	timeout = SAA_BUS_TIMEOUT;
155*4882a593Smuzhiyun 	curr_srp = saa7164_readl(bus->m_dwSetReadPos);
156*4882a593Smuzhiyun 	curr_swp = saa7164_readl(bus->m_dwSetWritePos);
157*4882a593Smuzhiyun 
158*4882a593Smuzhiyun 	/* Deal with ring wrapping issues */
159*4882a593Smuzhiyun 	if (curr_srp > curr_swp)
160*4882a593Smuzhiyun 		/* Deal with the wrapped ring */
161*4882a593Smuzhiyun 		free_write_space = curr_srp - curr_swp;
162*4882a593Smuzhiyun 	else
163*4882a593Smuzhiyun 		/* The ring has not wrapped yet */
164*4882a593Smuzhiyun 		free_write_space = (curr_srp + bus->m_dwSizeSetRing) - curr_swp;
165*4882a593Smuzhiyun 
166*4882a593Smuzhiyun 	dprintk(DBGLVL_BUS, "%s() bytes_to_write = %d\n", __func__,
167*4882a593Smuzhiyun 		bytes_to_write);
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun 	dprintk(DBGLVL_BUS, "%s() free_write_space = %d\n", __func__,
170*4882a593Smuzhiyun 		free_write_space);
171*4882a593Smuzhiyun 
172*4882a593Smuzhiyun 	dprintk(DBGLVL_BUS, "%s() curr_srp = %x\n", __func__, curr_srp);
173*4882a593Smuzhiyun 	dprintk(DBGLVL_BUS, "%s() curr_swp = %x\n", __func__, curr_swp);
174*4882a593Smuzhiyun 
175*4882a593Smuzhiyun 	/* Process the msg and write the content onto the bus */
176*4882a593Smuzhiyun 	while (bytes_to_write >= free_write_space) {
177*4882a593Smuzhiyun 
178*4882a593Smuzhiyun 		if (timeout-- == 0) {
179*4882a593Smuzhiyun 			printk(KERN_ERR "%s() bus timeout\n", __func__);
180*4882a593Smuzhiyun 			ret = SAA_ERR_NO_RESOURCES;
181*4882a593Smuzhiyun 			goto out;
182*4882a593Smuzhiyun 		}
183*4882a593Smuzhiyun 
184*4882a593Smuzhiyun 		/* TODO: Review this delay, efficient? */
185*4882a593Smuzhiyun 		/* Wait, allowing the hardware fetch time */
186*4882a593Smuzhiyun 		mdelay(1);
187*4882a593Smuzhiyun 
188*4882a593Smuzhiyun 		/* Check the space usage again */
189*4882a593Smuzhiyun 		curr_srp = saa7164_readl(bus->m_dwSetReadPos);
190*4882a593Smuzhiyun 
191*4882a593Smuzhiyun 		/* Deal with ring wrapping issues */
192*4882a593Smuzhiyun 		if (curr_srp > curr_swp)
193*4882a593Smuzhiyun 			/* Deal with the wrapped ring */
194*4882a593Smuzhiyun 			free_write_space = curr_srp - curr_swp;
195*4882a593Smuzhiyun 		else
196*4882a593Smuzhiyun 			/* Read didn't wrap around the buffer */
197*4882a593Smuzhiyun 			free_write_space = (curr_srp + bus->m_dwSizeSetRing) -
198*4882a593Smuzhiyun 				curr_swp;
199*4882a593Smuzhiyun 
200*4882a593Smuzhiyun 	}
201*4882a593Smuzhiyun 
202*4882a593Smuzhiyun 	/* Calculate the new write position */
203*4882a593Smuzhiyun 	new_swp = curr_swp + bytes_to_write;
204*4882a593Smuzhiyun 
205*4882a593Smuzhiyun 	dprintk(DBGLVL_BUS, "%s() new_swp = %x\n", __func__, new_swp);
206*4882a593Smuzhiyun 	dprintk(DBGLVL_BUS, "%s() bus->m_dwSizeSetRing = %x\n", __func__,
207*4882a593Smuzhiyun 		bus->m_dwSizeSetRing);
208*4882a593Smuzhiyun 
209*4882a593Smuzhiyun 	/*
210*4882a593Smuzhiyun 	 * Make a copy of msg->size before it is converted to le16 since it is
211*4882a593Smuzhiyun 	 * used in the code below.
212*4882a593Smuzhiyun 	 */
213*4882a593Smuzhiyun 	size = msg->size;
214*4882a593Smuzhiyun 	/* Convert to le16/le32 */
215*4882a593Smuzhiyun 	msg->size = (__force u16)cpu_to_le16(msg->size);
216*4882a593Smuzhiyun 	msg->command = (__force u32)cpu_to_le32(msg->command);
217*4882a593Smuzhiyun 	msg->controlselector = (__force u16)cpu_to_le16(msg->controlselector);
218*4882a593Smuzhiyun 
219*4882a593Smuzhiyun 	/* Mental Note: line 462 tmmhComResBusPCIe.cpp */
220*4882a593Smuzhiyun 
221*4882a593Smuzhiyun 	/* Check if we're going to wrap again */
222*4882a593Smuzhiyun 	if (new_swp > bus->m_dwSizeSetRing) {
223*4882a593Smuzhiyun 
224*4882a593Smuzhiyun 		/* Ring wraps */
225*4882a593Smuzhiyun 		new_swp -= bus->m_dwSizeSetRing;
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun 		space_rem = bus->m_dwSizeSetRing - curr_swp;
228*4882a593Smuzhiyun 
229*4882a593Smuzhiyun 		dprintk(DBGLVL_BUS, "%s() space_rem = %x\n", __func__,
230*4882a593Smuzhiyun 			space_rem);
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun 		dprintk(DBGLVL_BUS, "%s() sizeof(*msg) = %d\n", __func__,
233*4882a593Smuzhiyun 			(u32)sizeof(*msg));
234*4882a593Smuzhiyun 
235*4882a593Smuzhiyun 		if (space_rem < sizeof(*msg)) {
236*4882a593Smuzhiyun 			dprintk(DBGLVL_BUS, "%s() tr4\n", __func__);
237*4882a593Smuzhiyun 
238*4882a593Smuzhiyun 			/* Split the msg into pieces as the ring wraps */
239*4882a593Smuzhiyun 			memcpy_toio(bus->m_pdwSetRing + curr_swp, msg, space_rem);
240*4882a593Smuzhiyun 			memcpy_toio(bus->m_pdwSetRing, (u8 *)msg + space_rem,
241*4882a593Smuzhiyun 				sizeof(*msg) - space_rem);
242*4882a593Smuzhiyun 
243*4882a593Smuzhiyun 			memcpy_toio(bus->m_pdwSetRing + sizeof(*msg) - space_rem,
244*4882a593Smuzhiyun 				buf, size);
245*4882a593Smuzhiyun 
246*4882a593Smuzhiyun 		} else if (space_rem == sizeof(*msg)) {
247*4882a593Smuzhiyun 			dprintk(DBGLVL_BUS, "%s() tr5\n", __func__);
248*4882a593Smuzhiyun 
249*4882a593Smuzhiyun 			/* Additional data at the beginning of the ring */
250*4882a593Smuzhiyun 			memcpy_toio(bus->m_pdwSetRing + curr_swp, msg, sizeof(*msg));
251*4882a593Smuzhiyun 			memcpy_toio(bus->m_pdwSetRing, buf, size);
252*4882a593Smuzhiyun 
253*4882a593Smuzhiyun 		} else {
254*4882a593Smuzhiyun 			/* Additional data wraps around the ring */
255*4882a593Smuzhiyun 			memcpy_toio(bus->m_pdwSetRing + curr_swp, msg, sizeof(*msg));
256*4882a593Smuzhiyun 			if (size > 0) {
257*4882a593Smuzhiyun 				memcpy_toio(bus->m_pdwSetRing + curr_swp +
258*4882a593Smuzhiyun 					sizeof(*msg), buf, space_rem -
259*4882a593Smuzhiyun 					sizeof(*msg));
260*4882a593Smuzhiyun 				memcpy_toio(bus->m_pdwSetRing, (u8 *)buf +
261*4882a593Smuzhiyun 					space_rem - sizeof(*msg),
262*4882a593Smuzhiyun 					bytes_to_write - space_rem);
263*4882a593Smuzhiyun 			}
264*4882a593Smuzhiyun 
265*4882a593Smuzhiyun 		}
266*4882a593Smuzhiyun 
267*4882a593Smuzhiyun 	} /* (new_swp > bus->m_dwSizeSetRing) */
268*4882a593Smuzhiyun 	else {
269*4882a593Smuzhiyun 		dprintk(DBGLVL_BUS, "%s() tr6\n", __func__);
270*4882a593Smuzhiyun 
271*4882a593Smuzhiyun 		/* The ring buffer doesn't wrap, two simple copies */
272*4882a593Smuzhiyun 		memcpy_toio(bus->m_pdwSetRing + curr_swp, msg, sizeof(*msg));
273*4882a593Smuzhiyun 		memcpy_toio(bus->m_pdwSetRing + curr_swp + sizeof(*msg), buf,
274*4882a593Smuzhiyun 			size);
275*4882a593Smuzhiyun 	}
276*4882a593Smuzhiyun 
277*4882a593Smuzhiyun 	dprintk(DBGLVL_BUS, "%s() new_swp = %x\n", __func__, new_swp);
278*4882a593Smuzhiyun 
279*4882a593Smuzhiyun 	/* Update the bus write position */
280*4882a593Smuzhiyun 	saa7164_writel(bus->m_dwSetWritePos, new_swp);
281*4882a593Smuzhiyun 
282*4882a593Smuzhiyun 	/* Convert back to cpu after writing the msg to the ringbuffer. */
283*4882a593Smuzhiyun 	msg->size = le16_to_cpu((__force __le16)msg->size);
284*4882a593Smuzhiyun 	msg->command = le32_to_cpu((__force __le32)msg->command);
285*4882a593Smuzhiyun 	msg->controlselector = le16_to_cpu((__force __le16)msg->controlselector);
286*4882a593Smuzhiyun 	ret = SAA_OK;
287*4882a593Smuzhiyun 
288*4882a593Smuzhiyun out:
289*4882a593Smuzhiyun 	saa7164_bus_dump(dev);
290*4882a593Smuzhiyun 	mutex_unlock(&bus->lock);
291*4882a593Smuzhiyun 	saa7164_bus_verify(dev);
292*4882a593Smuzhiyun 	return ret;
293*4882a593Smuzhiyun }
294*4882a593Smuzhiyun 
295*4882a593Smuzhiyun /*
296*4882a593Smuzhiyun  * Receive a command or a response from the bus. The implementation does not
297*4882a593Smuzhiyun  * know if it is a command or a response it simply dequeues the data,
298*4882a593Smuzhiyun  * depending on the bus information given in the struct tmComResBusInfo
299*4882a593Smuzhiyun  * structure.
300*4882a593Smuzhiyun  *
301*4882a593Smuzhiyun  * Return Value:
302*4882a593Smuzhiyun  *  0          The function executed successfully.
303*4882a593Smuzhiyun  *  < 0        One or more members are not initialized.
304*4882a593Smuzhiyun  */
saa7164_bus_get(struct saa7164_dev * dev,struct tmComResInfo * msg,void * buf,int peekonly)305*4882a593Smuzhiyun int saa7164_bus_get(struct saa7164_dev *dev, struct tmComResInfo* msg,
306*4882a593Smuzhiyun 	void *buf, int peekonly)
307*4882a593Smuzhiyun {
308*4882a593Smuzhiyun 	struct tmComResBusInfo *bus = &dev->bus;
309*4882a593Smuzhiyun 	u32 bytes_to_read, write_distance, curr_grp, curr_gwp,
310*4882a593Smuzhiyun 		new_grp, buf_size, space_rem;
311*4882a593Smuzhiyun 	struct tmComResInfo msg_tmp;
312*4882a593Smuzhiyun 	int ret = SAA_ERR_BAD_PARAMETER;
313*4882a593Smuzhiyun 
314*4882a593Smuzhiyun 	saa7164_bus_verify(dev);
315*4882a593Smuzhiyun 
316*4882a593Smuzhiyun 	if (msg == NULL)
317*4882a593Smuzhiyun 		return ret;
318*4882a593Smuzhiyun 
319*4882a593Smuzhiyun 	if (msg->size > dev->bus.m_wMaxReqSize) {
320*4882a593Smuzhiyun 		printk(KERN_ERR "%s() Exceeded dev->bus.m_wMaxReqSize\n",
321*4882a593Smuzhiyun 			__func__);
322*4882a593Smuzhiyun 		return ret;
323*4882a593Smuzhiyun 	}
324*4882a593Smuzhiyun 
325*4882a593Smuzhiyun 	if ((peekonly == 0) && (msg->size > 0) && (buf == NULL)) {
326*4882a593Smuzhiyun 		printk(KERN_ERR
327*4882a593Smuzhiyun 			"%s() Missing msg buf, size should be %d bytes\n",
328*4882a593Smuzhiyun 			__func__, msg->size);
329*4882a593Smuzhiyun 		return ret;
330*4882a593Smuzhiyun 	}
331*4882a593Smuzhiyun 
332*4882a593Smuzhiyun 	mutex_lock(&bus->lock);
333*4882a593Smuzhiyun 
334*4882a593Smuzhiyun 	/* Peek the bus to see if a msg exists, if it's not what we're expecting
335*4882a593Smuzhiyun 	 * then return cleanly else read the message from the bus.
336*4882a593Smuzhiyun 	 */
337*4882a593Smuzhiyun 	curr_gwp = saa7164_readl(bus->m_dwGetWritePos);
338*4882a593Smuzhiyun 	curr_grp = saa7164_readl(bus->m_dwGetReadPos);
339*4882a593Smuzhiyun 
340*4882a593Smuzhiyun 	if (curr_gwp == curr_grp) {
341*4882a593Smuzhiyun 		ret = SAA_ERR_EMPTY;
342*4882a593Smuzhiyun 		goto out;
343*4882a593Smuzhiyun 	}
344*4882a593Smuzhiyun 
345*4882a593Smuzhiyun 	bytes_to_read = sizeof(*msg);
346*4882a593Smuzhiyun 
347*4882a593Smuzhiyun 	/* Calculate write distance to current read position */
348*4882a593Smuzhiyun 	write_distance = 0;
349*4882a593Smuzhiyun 	if (curr_gwp >= curr_grp)
350*4882a593Smuzhiyun 		/* Write doesn't wrap around the ring */
351*4882a593Smuzhiyun 		write_distance = curr_gwp - curr_grp;
352*4882a593Smuzhiyun 	else
353*4882a593Smuzhiyun 		/* Write wraps around the ring */
354*4882a593Smuzhiyun 		write_distance = curr_gwp + bus->m_dwSizeGetRing - curr_grp;
355*4882a593Smuzhiyun 
356*4882a593Smuzhiyun 	if (bytes_to_read > write_distance) {
357*4882a593Smuzhiyun 		printk(KERN_ERR "%s() No message/response found\n", __func__);
358*4882a593Smuzhiyun 		ret = SAA_ERR_INVALID_COMMAND;
359*4882a593Smuzhiyun 		goto out;
360*4882a593Smuzhiyun 	}
361*4882a593Smuzhiyun 
362*4882a593Smuzhiyun 	/* Calculate the new read position */
363*4882a593Smuzhiyun 	new_grp = curr_grp + bytes_to_read;
364*4882a593Smuzhiyun 	if (new_grp > bus->m_dwSizeGetRing) {
365*4882a593Smuzhiyun 
366*4882a593Smuzhiyun 		/* Ring wraps */
367*4882a593Smuzhiyun 		new_grp -= bus->m_dwSizeGetRing;
368*4882a593Smuzhiyun 		space_rem = bus->m_dwSizeGetRing - curr_grp;
369*4882a593Smuzhiyun 
370*4882a593Smuzhiyun 		memcpy_fromio(&msg_tmp, bus->m_pdwGetRing + curr_grp, space_rem);
371*4882a593Smuzhiyun 		memcpy_fromio((u8 *)&msg_tmp + space_rem, bus->m_pdwGetRing,
372*4882a593Smuzhiyun 			bytes_to_read - space_rem);
373*4882a593Smuzhiyun 
374*4882a593Smuzhiyun 	} else {
375*4882a593Smuzhiyun 		/* No wrapping */
376*4882a593Smuzhiyun 		memcpy_fromio(&msg_tmp, bus->m_pdwGetRing + curr_grp, bytes_to_read);
377*4882a593Smuzhiyun 	}
378*4882a593Smuzhiyun 	/* Convert from little endian to CPU */
379*4882a593Smuzhiyun 	msg_tmp.size = le16_to_cpu((__force __le16)msg_tmp.size);
380*4882a593Smuzhiyun 	msg_tmp.command = le32_to_cpu((__force __le32)msg_tmp.command);
381*4882a593Smuzhiyun 	msg_tmp.controlselector = le16_to_cpu((__force __le16)msg_tmp.controlselector);
382*4882a593Smuzhiyun 	memcpy(msg, &msg_tmp, sizeof(*msg));
383*4882a593Smuzhiyun 
384*4882a593Smuzhiyun 	/* No need to update the read positions, because this was a peek */
385*4882a593Smuzhiyun 	/* If the caller specifically want to peek, return */
386*4882a593Smuzhiyun 	if (peekonly) {
387*4882a593Smuzhiyun 		goto peekout;
388*4882a593Smuzhiyun 	}
389*4882a593Smuzhiyun 
390*4882a593Smuzhiyun 	/* Check if the command/response matches what is expected */
391*4882a593Smuzhiyun 	if ((msg_tmp.id != msg->id) || (msg_tmp.command != msg->command) ||
392*4882a593Smuzhiyun 		(msg_tmp.controlselector != msg->controlselector) ||
393*4882a593Smuzhiyun 		(msg_tmp.seqno != msg->seqno) || (msg_tmp.size != msg->size)) {
394*4882a593Smuzhiyun 
395*4882a593Smuzhiyun 		printk(KERN_ERR "%s() Unexpected msg miss-match\n", __func__);
396*4882a593Smuzhiyun 		saa7164_bus_dumpmsg(dev, msg, buf);
397*4882a593Smuzhiyun 		saa7164_bus_dumpmsg(dev, &msg_tmp, NULL);
398*4882a593Smuzhiyun 		ret = SAA_ERR_INVALID_COMMAND;
399*4882a593Smuzhiyun 		goto out;
400*4882a593Smuzhiyun 	}
401*4882a593Smuzhiyun 
402*4882a593Smuzhiyun 	/* Get the actual command and response from the bus */
403*4882a593Smuzhiyun 	buf_size = msg->size;
404*4882a593Smuzhiyun 
405*4882a593Smuzhiyun 	bytes_to_read = sizeof(*msg) + msg->size;
406*4882a593Smuzhiyun 	/* Calculate write distance to current read position */
407*4882a593Smuzhiyun 	write_distance = 0;
408*4882a593Smuzhiyun 	if (curr_gwp >= curr_grp)
409*4882a593Smuzhiyun 		/* Write doesn't wrap around the ring */
410*4882a593Smuzhiyun 		write_distance = curr_gwp - curr_grp;
411*4882a593Smuzhiyun 	else
412*4882a593Smuzhiyun 		/* Write wraps around the ring */
413*4882a593Smuzhiyun 		write_distance = curr_gwp + bus->m_dwSizeGetRing - curr_grp;
414*4882a593Smuzhiyun 
415*4882a593Smuzhiyun 	if (bytes_to_read > write_distance) {
416*4882a593Smuzhiyun 		printk(KERN_ERR "%s() Invalid bus state, missing msg or mangled ring, faulty H/W / bad code?\n",
417*4882a593Smuzhiyun 		       __func__);
418*4882a593Smuzhiyun 		ret = SAA_ERR_INVALID_COMMAND;
419*4882a593Smuzhiyun 		goto out;
420*4882a593Smuzhiyun 	}
421*4882a593Smuzhiyun 
422*4882a593Smuzhiyun 	/* Calculate the new read position */
423*4882a593Smuzhiyun 	new_grp = curr_grp + bytes_to_read;
424*4882a593Smuzhiyun 	if (new_grp > bus->m_dwSizeGetRing) {
425*4882a593Smuzhiyun 
426*4882a593Smuzhiyun 		/* Ring wraps */
427*4882a593Smuzhiyun 		new_grp -= bus->m_dwSizeGetRing;
428*4882a593Smuzhiyun 		space_rem = bus->m_dwSizeGetRing - curr_grp;
429*4882a593Smuzhiyun 
430*4882a593Smuzhiyun 		if (space_rem < sizeof(*msg)) {
431*4882a593Smuzhiyun 			if (buf)
432*4882a593Smuzhiyun 				memcpy_fromio(buf, bus->m_pdwGetRing + sizeof(*msg) -
433*4882a593Smuzhiyun 					space_rem, buf_size);
434*4882a593Smuzhiyun 
435*4882a593Smuzhiyun 		} else if (space_rem == sizeof(*msg)) {
436*4882a593Smuzhiyun 			if (buf)
437*4882a593Smuzhiyun 				memcpy_fromio(buf, bus->m_pdwGetRing, buf_size);
438*4882a593Smuzhiyun 		} else {
439*4882a593Smuzhiyun 			/* Additional data wraps around the ring */
440*4882a593Smuzhiyun 			if (buf) {
441*4882a593Smuzhiyun 				memcpy_fromio(buf, bus->m_pdwGetRing + curr_grp +
442*4882a593Smuzhiyun 					sizeof(*msg), space_rem - sizeof(*msg));
443*4882a593Smuzhiyun 				memcpy_fromio(buf + space_rem - sizeof(*msg),
444*4882a593Smuzhiyun 					bus->m_pdwGetRing, bytes_to_read -
445*4882a593Smuzhiyun 					space_rem);
446*4882a593Smuzhiyun 			}
447*4882a593Smuzhiyun 
448*4882a593Smuzhiyun 		}
449*4882a593Smuzhiyun 
450*4882a593Smuzhiyun 	} else {
451*4882a593Smuzhiyun 		/* No wrapping */
452*4882a593Smuzhiyun 		if (buf)
453*4882a593Smuzhiyun 			memcpy_fromio(buf, bus->m_pdwGetRing + curr_grp + sizeof(*msg),
454*4882a593Smuzhiyun 				buf_size);
455*4882a593Smuzhiyun 	}
456*4882a593Smuzhiyun 
457*4882a593Smuzhiyun 	/* Update the read positions, adjusting the ring */
458*4882a593Smuzhiyun 	saa7164_writel(bus->m_dwGetReadPos, new_grp);
459*4882a593Smuzhiyun 
460*4882a593Smuzhiyun peekout:
461*4882a593Smuzhiyun 	ret = SAA_OK;
462*4882a593Smuzhiyun out:
463*4882a593Smuzhiyun 	mutex_unlock(&bus->lock);
464*4882a593Smuzhiyun 	saa7164_bus_verify(dev);
465*4882a593Smuzhiyun 	return ret;
466*4882a593Smuzhiyun }
467*4882a593Smuzhiyun 
468