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