18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Driver for the NXP SAA7164 PCIe bridge
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (c) 2010-2015 Steven Toth <stoth@kernellabs.com>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include "saa7164.h"
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci/* The message bus to/from the firmware is a ring buffer in PCI address
118c2ecf20Sopenharmony_ci * space. Establish the defaults.
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ciint saa7164_bus_setup(struct saa7164_dev *dev)
148c2ecf20Sopenharmony_ci{
158c2ecf20Sopenharmony_ci	struct tmComResBusInfo *b	= &dev->bus;
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci	mutex_init(&b->lock);
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci	b->Type			= TYPE_BUS_PCIe;
208c2ecf20Sopenharmony_ci	b->m_wMaxReqSize	= SAA_DEVICE_MAXREQUESTSIZE;
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci	b->m_pdwSetRing		= (u8 __iomem *)(dev->bmmio +
238c2ecf20Sopenharmony_ci		((u32)dev->busdesc.CommandRing));
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci	b->m_dwSizeSetRing	= SAA_DEVICE_BUFFERBLOCKSIZE;
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci	b->m_pdwGetRing		= (u8 __iomem *)(dev->bmmio +
288c2ecf20Sopenharmony_ci		((u32)dev->busdesc.ResponseRing));
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	b->m_dwSizeGetRing	= SAA_DEVICE_BUFFERBLOCKSIZE;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	b->m_dwSetWritePos	= ((u32)dev->intfdesc.BARLocation) +
338c2ecf20Sopenharmony_ci		(2 * sizeof(u64));
348c2ecf20Sopenharmony_ci	b->m_dwSetReadPos	= b->m_dwSetWritePos + (1 * sizeof(u32));
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	b->m_dwGetWritePos	= b->m_dwSetWritePos + (2 * sizeof(u32));
378c2ecf20Sopenharmony_ci	b->m_dwGetReadPos	= b->m_dwSetWritePos + (3 * sizeof(u32));
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	return 0;
408c2ecf20Sopenharmony_ci}
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_civoid saa7164_bus_dump(struct saa7164_dev *dev)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	struct tmComResBusInfo *b = &dev->bus;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	dprintk(DBGLVL_BUS, "Dumping the bus structure:\n");
478c2ecf20Sopenharmony_ci	dprintk(DBGLVL_BUS, " .type             = %d\n", b->Type);
488c2ecf20Sopenharmony_ci	dprintk(DBGLVL_BUS, " .dev->bmmio       = 0x%p\n", dev->bmmio);
498c2ecf20Sopenharmony_ci	dprintk(DBGLVL_BUS, " .m_wMaxReqSize    = 0x%x\n", b->m_wMaxReqSize);
508c2ecf20Sopenharmony_ci	dprintk(DBGLVL_BUS, " .m_pdwSetRing     = 0x%p\n", b->m_pdwSetRing);
518c2ecf20Sopenharmony_ci	dprintk(DBGLVL_BUS, " .m_dwSizeSetRing  = 0x%x\n", b->m_dwSizeSetRing);
528c2ecf20Sopenharmony_ci	dprintk(DBGLVL_BUS, " .m_pdwGetRing     = 0x%p\n", b->m_pdwGetRing);
538c2ecf20Sopenharmony_ci	dprintk(DBGLVL_BUS, " .m_dwSizeGetRing  = 0x%x\n", b->m_dwSizeGetRing);
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	dprintk(DBGLVL_BUS, " .m_dwSetReadPos   = 0x%x (0x%08x)\n",
568c2ecf20Sopenharmony_ci		b->m_dwSetReadPos, saa7164_readl(b->m_dwSetReadPos));
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	dprintk(DBGLVL_BUS, " .m_dwSetWritePos  = 0x%x (0x%08x)\n",
598c2ecf20Sopenharmony_ci		b->m_dwSetWritePos, saa7164_readl(b->m_dwSetWritePos));
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	dprintk(DBGLVL_BUS, " .m_dwGetReadPos   = 0x%x (0x%08x)\n",
628c2ecf20Sopenharmony_ci		b->m_dwGetReadPos, saa7164_readl(b->m_dwGetReadPos));
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	dprintk(DBGLVL_BUS, " .m_dwGetWritePos  = 0x%x (0x%08x)\n",
658c2ecf20Sopenharmony_ci		b->m_dwGetWritePos, saa7164_readl(b->m_dwGetWritePos));
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci/* Intensionally throw a BUG() if the state of the message bus looks corrupt */
708c2ecf20Sopenharmony_cistatic void saa7164_bus_verify(struct saa7164_dev *dev)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	struct tmComResBusInfo *b = &dev->bus;
738c2ecf20Sopenharmony_ci	int bug = 0;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	if (saa7164_readl(b->m_dwSetReadPos) > b->m_dwSizeSetRing)
768c2ecf20Sopenharmony_ci		bug++;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	if (saa7164_readl(b->m_dwSetWritePos) > b->m_dwSizeSetRing)
798c2ecf20Sopenharmony_ci		bug++;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	if (saa7164_readl(b->m_dwGetReadPos) > b->m_dwSizeGetRing)
828c2ecf20Sopenharmony_ci		bug++;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	if (saa7164_readl(b->m_dwGetWritePos) > b->m_dwSizeGetRing)
858c2ecf20Sopenharmony_ci		bug++;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	if (bug) {
888c2ecf20Sopenharmony_ci		saa_debug = 0xffff; /* Ensure we get the bus dump */
898c2ecf20Sopenharmony_ci		saa7164_bus_dump(dev);
908c2ecf20Sopenharmony_ci		saa_debug = 1024; /* Ensure we get the bus dump */
918c2ecf20Sopenharmony_ci		BUG();
928c2ecf20Sopenharmony_ci	}
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic void saa7164_bus_dumpmsg(struct saa7164_dev *dev, struct tmComResInfo *m,
968c2ecf20Sopenharmony_ci				void *buf)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	dprintk(DBGLVL_BUS, "Dumping msg structure:\n");
998c2ecf20Sopenharmony_ci	dprintk(DBGLVL_BUS, " .id               = %d\n",   m->id);
1008c2ecf20Sopenharmony_ci	dprintk(DBGLVL_BUS, " .flags            = 0x%x\n", m->flags);
1018c2ecf20Sopenharmony_ci	dprintk(DBGLVL_BUS, " .size             = 0x%x\n", m->size);
1028c2ecf20Sopenharmony_ci	dprintk(DBGLVL_BUS, " .command          = 0x%x\n", m->command);
1038c2ecf20Sopenharmony_ci	dprintk(DBGLVL_BUS, " .controlselector  = 0x%x\n", m->controlselector);
1048c2ecf20Sopenharmony_ci	dprintk(DBGLVL_BUS, " .seqno            = %d\n",   m->seqno);
1058c2ecf20Sopenharmony_ci	if (buf)
1068c2ecf20Sopenharmony_ci		dprintk(DBGLVL_BUS, " .buffer (ignored)\n");
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci/*
1108c2ecf20Sopenharmony_ci * Places a command or a response on the bus. The implementation does not
1118c2ecf20Sopenharmony_ci * know if it is a command or a response it just places the data on the
1128c2ecf20Sopenharmony_ci * bus depending on the bus information given in the struct tmComResBusInfo
1138c2ecf20Sopenharmony_ci * structure. If the command or response does not fit into the bus ring
1148c2ecf20Sopenharmony_ci * buffer it will be refused.
1158c2ecf20Sopenharmony_ci *
1168c2ecf20Sopenharmony_ci * Return Value:
1178c2ecf20Sopenharmony_ci *  SAA_OK     The function executed successfully.
1188c2ecf20Sopenharmony_ci *  < 0        One or more members are not initialized.
1198c2ecf20Sopenharmony_ci */
1208c2ecf20Sopenharmony_ciint saa7164_bus_set(struct saa7164_dev *dev, struct tmComResInfo* msg,
1218c2ecf20Sopenharmony_ci	void *buf)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	struct tmComResBusInfo *bus = &dev->bus;
1248c2ecf20Sopenharmony_ci	u32 bytes_to_write, free_write_space, timeout, curr_srp, curr_swp;
1258c2ecf20Sopenharmony_ci	u32 new_swp, space_rem;
1268c2ecf20Sopenharmony_ci	int ret = SAA_ERR_BAD_PARAMETER;
1278c2ecf20Sopenharmony_ci	u16 size;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	if (!msg) {
1308c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s() !msg\n", __func__);
1318c2ecf20Sopenharmony_ci		return SAA_ERR_BAD_PARAMETER;
1328c2ecf20Sopenharmony_ci	}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	dprintk(DBGLVL_BUS, "%s()\n", __func__);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	saa7164_bus_verify(dev);
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	if (msg->size > dev->bus.m_wMaxReqSize) {
1398c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s() Exceeded dev->bus.m_wMaxReqSize\n",
1408c2ecf20Sopenharmony_ci			__func__);
1418c2ecf20Sopenharmony_ci		return SAA_ERR_BAD_PARAMETER;
1428c2ecf20Sopenharmony_ci	}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	if ((msg->size > 0) && (buf == NULL)) {
1458c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s() Missing message buffer\n", __func__);
1468c2ecf20Sopenharmony_ci		return SAA_ERR_BAD_PARAMETER;
1478c2ecf20Sopenharmony_ci	}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	/* Lock the bus from any other access */
1508c2ecf20Sopenharmony_ci	mutex_lock(&bus->lock);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	bytes_to_write = sizeof(*msg) + msg->size;
1538c2ecf20Sopenharmony_ci	free_write_space = 0;
1548c2ecf20Sopenharmony_ci	timeout = SAA_BUS_TIMEOUT;
1558c2ecf20Sopenharmony_ci	curr_srp = saa7164_readl(bus->m_dwSetReadPos);
1568c2ecf20Sopenharmony_ci	curr_swp = saa7164_readl(bus->m_dwSetWritePos);
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	/* Deal with ring wrapping issues */
1598c2ecf20Sopenharmony_ci	if (curr_srp > curr_swp)
1608c2ecf20Sopenharmony_ci		/* Deal with the wrapped ring */
1618c2ecf20Sopenharmony_ci		free_write_space = curr_srp - curr_swp;
1628c2ecf20Sopenharmony_ci	else
1638c2ecf20Sopenharmony_ci		/* The ring has not wrapped yet */
1648c2ecf20Sopenharmony_ci		free_write_space = (curr_srp + bus->m_dwSizeSetRing) - curr_swp;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	dprintk(DBGLVL_BUS, "%s() bytes_to_write = %d\n", __func__,
1678c2ecf20Sopenharmony_ci		bytes_to_write);
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	dprintk(DBGLVL_BUS, "%s() free_write_space = %d\n", __func__,
1708c2ecf20Sopenharmony_ci		free_write_space);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	dprintk(DBGLVL_BUS, "%s() curr_srp = %x\n", __func__, curr_srp);
1738c2ecf20Sopenharmony_ci	dprintk(DBGLVL_BUS, "%s() curr_swp = %x\n", __func__, curr_swp);
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	/* Process the msg and write the content onto the bus */
1768c2ecf20Sopenharmony_ci	while (bytes_to_write >= free_write_space) {
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci		if (timeout-- == 0) {
1798c2ecf20Sopenharmony_ci			printk(KERN_ERR "%s() bus timeout\n", __func__);
1808c2ecf20Sopenharmony_ci			ret = SAA_ERR_NO_RESOURCES;
1818c2ecf20Sopenharmony_ci			goto out;
1828c2ecf20Sopenharmony_ci		}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci		/* TODO: Review this delay, efficient? */
1858c2ecf20Sopenharmony_ci		/* Wait, allowing the hardware fetch time */
1868c2ecf20Sopenharmony_ci		mdelay(1);
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci		/* Check the space usage again */
1898c2ecf20Sopenharmony_ci		curr_srp = saa7164_readl(bus->m_dwSetReadPos);
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci		/* Deal with ring wrapping issues */
1928c2ecf20Sopenharmony_ci		if (curr_srp > curr_swp)
1938c2ecf20Sopenharmony_ci			/* Deal with the wrapped ring */
1948c2ecf20Sopenharmony_ci			free_write_space = curr_srp - curr_swp;
1958c2ecf20Sopenharmony_ci		else
1968c2ecf20Sopenharmony_ci			/* Read didn't wrap around the buffer */
1978c2ecf20Sopenharmony_ci			free_write_space = (curr_srp + bus->m_dwSizeSetRing) -
1988c2ecf20Sopenharmony_ci				curr_swp;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	/* Calculate the new write position */
2038c2ecf20Sopenharmony_ci	new_swp = curr_swp + bytes_to_write;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	dprintk(DBGLVL_BUS, "%s() new_swp = %x\n", __func__, new_swp);
2068c2ecf20Sopenharmony_ci	dprintk(DBGLVL_BUS, "%s() bus->m_dwSizeSetRing = %x\n", __func__,
2078c2ecf20Sopenharmony_ci		bus->m_dwSizeSetRing);
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	/*
2108c2ecf20Sopenharmony_ci	 * Make a copy of msg->size before it is converted to le16 since it is
2118c2ecf20Sopenharmony_ci	 * used in the code below.
2128c2ecf20Sopenharmony_ci	 */
2138c2ecf20Sopenharmony_ci	size = msg->size;
2148c2ecf20Sopenharmony_ci	/* Convert to le16/le32 */
2158c2ecf20Sopenharmony_ci	msg->size = (__force u16)cpu_to_le16(msg->size);
2168c2ecf20Sopenharmony_ci	msg->command = (__force u32)cpu_to_le32(msg->command);
2178c2ecf20Sopenharmony_ci	msg->controlselector = (__force u16)cpu_to_le16(msg->controlselector);
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	/* Mental Note: line 462 tmmhComResBusPCIe.cpp */
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	/* Check if we're going to wrap again */
2228c2ecf20Sopenharmony_ci	if (new_swp > bus->m_dwSizeSetRing) {
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci		/* Ring wraps */
2258c2ecf20Sopenharmony_ci		new_swp -= bus->m_dwSizeSetRing;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci		space_rem = bus->m_dwSizeSetRing - curr_swp;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci		dprintk(DBGLVL_BUS, "%s() space_rem = %x\n", __func__,
2308c2ecf20Sopenharmony_ci			space_rem);
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci		dprintk(DBGLVL_BUS, "%s() sizeof(*msg) = %d\n", __func__,
2338c2ecf20Sopenharmony_ci			(u32)sizeof(*msg));
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci		if (space_rem < sizeof(*msg)) {
2368c2ecf20Sopenharmony_ci			dprintk(DBGLVL_BUS, "%s() tr4\n", __func__);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci			/* Split the msg into pieces as the ring wraps */
2398c2ecf20Sopenharmony_ci			memcpy_toio(bus->m_pdwSetRing + curr_swp, msg, space_rem);
2408c2ecf20Sopenharmony_ci			memcpy_toio(bus->m_pdwSetRing, (u8 *)msg + space_rem,
2418c2ecf20Sopenharmony_ci				sizeof(*msg) - space_rem);
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci			memcpy_toio(bus->m_pdwSetRing + sizeof(*msg) - space_rem,
2448c2ecf20Sopenharmony_ci				buf, size);
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci		} else if (space_rem == sizeof(*msg)) {
2478c2ecf20Sopenharmony_ci			dprintk(DBGLVL_BUS, "%s() tr5\n", __func__);
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci			/* Additional data at the beginning of the ring */
2508c2ecf20Sopenharmony_ci			memcpy_toio(bus->m_pdwSetRing + curr_swp, msg, sizeof(*msg));
2518c2ecf20Sopenharmony_ci			memcpy_toio(bus->m_pdwSetRing, buf, size);
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci		} else {
2548c2ecf20Sopenharmony_ci			/* Additional data wraps around the ring */
2558c2ecf20Sopenharmony_ci			memcpy_toio(bus->m_pdwSetRing + curr_swp, msg, sizeof(*msg));
2568c2ecf20Sopenharmony_ci			if (size > 0) {
2578c2ecf20Sopenharmony_ci				memcpy_toio(bus->m_pdwSetRing + curr_swp +
2588c2ecf20Sopenharmony_ci					sizeof(*msg), buf, space_rem -
2598c2ecf20Sopenharmony_ci					sizeof(*msg));
2608c2ecf20Sopenharmony_ci				memcpy_toio(bus->m_pdwSetRing, (u8 *)buf +
2618c2ecf20Sopenharmony_ci					space_rem - sizeof(*msg),
2628c2ecf20Sopenharmony_ci					bytes_to_write - space_rem);
2638c2ecf20Sopenharmony_ci			}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci		}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	} /* (new_swp > bus->m_dwSizeSetRing) */
2688c2ecf20Sopenharmony_ci	else {
2698c2ecf20Sopenharmony_ci		dprintk(DBGLVL_BUS, "%s() tr6\n", __func__);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci		/* The ring buffer doesn't wrap, two simple copies */
2728c2ecf20Sopenharmony_ci		memcpy_toio(bus->m_pdwSetRing + curr_swp, msg, sizeof(*msg));
2738c2ecf20Sopenharmony_ci		memcpy_toio(bus->m_pdwSetRing + curr_swp + sizeof(*msg), buf,
2748c2ecf20Sopenharmony_ci			size);
2758c2ecf20Sopenharmony_ci	}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	dprintk(DBGLVL_BUS, "%s() new_swp = %x\n", __func__, new_swp);
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	/* Update the bus write position */
2808c2ecf20Sopenharmony_ci	saa7164_writel(bus->m_dwSetWritePos, new_swp);
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	/* Convert back to cpu after writing the msg to the ringbuffer. */
2838c2ecf20Sopenharmony_ci	msg->size = le16_to_cpu((__force __le16)msg->size);
2848c2ecf20Sopenharmony_ci	msg->command = le32_to_cpu((__force __le32)msg->command);
2858c2ecf20Sopenharmony_ci	msg->controlselector = le16_to_cpu((__force __le16)msg->controlselector);
2868c2ecf20Sopenharmony_ci	ret = SAA_OK;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ciout:
2898c2ecf20Sopenharmony_ci	saa7164_bus_dump(dev);
2908c2ecf20Sopenharmony_ci	mutex_unlock(&bus->lock);
2918c2ecf20Sopenharmony_ci	saa7164_bus_verify(dev);
2928c2ecf20Sopenharmony_ci	return ret;
2938c2ecf20Sopenharmony_ci}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci/*
2968c2ecf20Sopenharmony_ci * Receive a command or a response from the bus. The implementation does not
2978c2ecf20Sopenharmony_ci * know if it is a command or a response it simply dequeues the data,
2988c2ecf20Sopenharmony_ci * depending on the bus information given in the struct tmComResBusInfo
2998c2ecf20Sopenharmony_ci * structure.
3008c2ecf20Sopenharmony_ci *
3018c2ecf20Sopenharmony_ci * Return Value:
3028c2ecf20Sopenharmony_ci *  0          The function executed successfully.
3038c2ecf20Sopenharmony_ci *  < 0        One or more members are not initialized.
3048c2ecf20Sopenharmony_ci */
3058c2ecf20Sopenharmony_ciint saa7164_bus_get(struct saa7164_dev *dev, struct tmComResInfo* msg,
3068c2ecf20Sopenharmony_ci	void *buf, int peekonly)
3078c2ecf20Sopenharmony_ci{
3088c2ecf20Sopenharmony_ci	struct tmComResBusInfo *bus = &dev->bus;
3098c2ecf20Sopenharmony_ci	u32 bytes_to_read, write_distance, curr_grp, curr_gwp,
3108c2ecf20Sopenharmony_ci		new_grp, buf_size, space_rem;
3118c2ecf20Sopenharmony_ci	struct tmComResInfo msg_tmp;
3128c2ecf20Sopenharmony_ci	int ret = SAA_ERR_BAD_PARAMETER;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	saa7164_bus_verify(dev);
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	if (msg == NULL)
3178c2ecf20Sopenharmony_ci		return ret;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	if (msg->size > dev->bus.m_wMaxReqSize) {
3208c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s() Exceeded dev->bus.m_wMaxReqSize\n",
3218c2ecf20Sopenharmony_ci			__func__);
3228c2ecf20Sopenharmony_ci		return ret;
3238c2ecf20Sopenharmony_ci	}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	if ((peekonly == 0) && (msg->size > 0) && (buf == NULL)) {
3268c2ecf20Sopenharmony_ci		printk(KERN_ERR
3278c2ecf20Sopenharmony_ci			"%s() Missing msg buf, size should be %d bytes\n",
3288c2ecf20Sopenharmony_ci			__func__, msg->size);
3298c2ecf20Sopenharmony_ci		return ret;
3308c2ecf20Sopenharmony_ci	}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	mutex_lock(&bus->lock);
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	/* Peek the bus to see if a msg exists, if it's not what we're expecting
3358c2ecf20Sopenharmony_ci	 * then return cleanly else read the message from the bus.
3368c2ecf20Sopenharmony_ci	 */
3378c2ecf20Sopenharmony_ci	curr_gwp = saa7164_readl(bus->m_dwGetWritePos);
3388c2ecf20Sopenharmony_ci	curr_grp = saa7164_readl(bus->m_dwGetReadPos);
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	if (curr_gwp == curr_grp) {
3418c2ecf20Sopenharmony_ci		ret = SAA_ERR_EMPTY;
3428c2ecf20Sopenharmony_ci		goto out;
3438c2ecf20Sopenharmony_ci	}
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	bytes_to_read = sizeof(*msg);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	/* Calculate write distance to current read position */
3488c2ecf20Sopenharmony_ci	write_distance = 0;
3498c2ecf20Sopenharmony_ci	if (curr_gwp >= curr_grp)
3508c2ecf20Sopenharmony_ci		/* Write doesn't wrap around the ring */
3518c2ecf20Sopenharmony_ci		write_distance = curr_gwp - curr_grp;
3528c2ecf20Sopenharmony_ci	else
3538c2ecf20Sopenharmony_ci		/* Write wraps around the ring */
3548c2ecf20Sopenharmony_ci		write_distance = curr_gwp + bus->m_dwSizeGetRing - curr_grp;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	if (bytes_to_read > write_distance) {
3578c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s() No message/response found\n", __func__);
3588c2ecf20Sopenharmony_ci		ret = SAA_ERR_INVALID_COMMAND;
3598c2ecf20Sopenharmony_ci		goto out;
3608c2ecf20Sopenharmony_ci	}
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	/* Calculate the new read position */
3638c2ecf20Sopenharmony_ci	new_grp = curr_grp + bytes_to_read;
3648c2ecf20Sopenharmony_ci	if (new_grp > bus->m_dwSizeGetRing) {
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci		/* Ring wraps */
3678c2ecf20Sopenharmony_ci		new_grp -= bus->m_dwSizeGetRing;
3688c2ecf20Sopenharmony_ci		space_rem = bus->m_dwSizeGetRing - curr_grp;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci		memcpy_fromio(&msg_tmp, bus->m_pdwGetRing + curr_grp, space_rem);
3718c2ecf20Sopenharmony_ci		memcpy_fromio((u8 *)&msg_tmp + space_rem, bus->m_pdwGetRing,
3728c2ecf20Sopenharmony_ci			bytes_to_read - space_rem);
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	} else {
3758c2ecf20Sopenharmony_ci		/* No wrapping */
3768c2ecf20Sopenharmony_ci		memcpy_fromio(&msg_tmp, bus->m_pdwGetRing + curr_grp, bytes_to_read);
3778c2ecf20Sopenharmony_ci	}
3788c2ecf20Sopenharmony_ci	/* Convert from little endian to CPU */
3798c2ecf20Sopenharmony_ci	msg_tmp.size = le16_to_cpu((__force __le16)msg_tmp.size);
3808c2ecf20Sopenharmony_ci	msg_tmp.command = le32_to_cpu((__force __le32)msg_tmp.command);
3818c2ecf20Sopenharmony_ci	msg_tmp.controlselector = le16_to_cpu((__force __le16)msg_tmp.controlselector);
3828c2ecf20Sopenharmony_ci	memcpy(msg, &msg_tmp, sizeof(*msg));
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	/* No need to update the read positions, because this was a peek */
3858c2ecf20Sopenharmony_ci	/* If the caller specifically want to peek, return */
3868c2ecf20Sopenharmony_ci	if (peekonly) {
3878c2ecf20Sopenharmony_ci		goto peekout;
3888c2ecf20Sopenharmony_ci	}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	/* Check if the command/response matches what is expected */
3918c2ecf20Sopenharmony_ci	if ((msg_tmp.id != msg->id) || (msg_tmp.command != msg->command) ||
3928c2ecf20Sopenharmony_ci		(msg_tmp.controlselector != msg->controlselector) ||
3938c2ecf20Sopenharmony_ci		(msg_tmp.seqno != msg->seqno) || (msg_tmp.size != msg->size)) {
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s() Unexpected msg miss-match\n", __func__);
3968c2ecf20Sopenharmony_ci		saa7164_bus_dumpmsg(dev, msg, buf);
3978c2ecf20Sopenharmony_ci		saa7164_bus_dumpmsg(dev, &msg_tmp, NULL);
3988c2ecf20Sopenharmony_ci		ret = SAA_ERR_INVALID_COMMAND;
3998c2ecf20Sopenharmony_ci		goto out;
4008c2ecf20Sopenharmony_ci	}
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	/* Get the actual command and response from the bus */
4038c2ecf20Sopenharmony_ci	buf_size = msg->size;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	bytes_to_read = sizeof(*msg) + msg->size;
4068c2ecf20Sopenharmony_ci	/* Calculate write distance to current read position */
4078c2ecf20Sopenharmony_ci	write_distance = 0;
4088c2ecf20Sopenharmony_ci	if (curr_gwp >= curr_grp)
4098c2ecf20Sopenharmony_ci		/* Write doesn't wrap around the ring */
4108c2ecf20Sopenharmony_ci		write_distance = curr_gwp - curr_grp;
4118c2ecf20Sopenharmony_ci	else
4128c2ecf20Sopenharmony_ci		/* Write wraps around the ring */
4138c2ecf20Sopenharmony_ci		write_distance = curr_gwp + bus->m_dwSizeGetRing - curr_grp;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	if (bytes_to_read > write_distance) {
4168c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s() Invalid bus state, missing msg or mangled ring, faulty H/W / bad code?\n",
4178c2ecf20Sopenharmony_ci		       __func__);
4188c2ecf20Sopenharmony_ci		ret = SAA_ERR_INVALID_COMMAND;
4198c2ecf20Sopenharmony_ci		goto out;
4208c2ecf20Sopenharmony_ci	}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	/* Calculate the new read position */
4238c2ecf20Sopenharmony_ci	new_grp = curr_grp + bytes_to_read;
4248c2ecf20Sopenharmony_ci	if (new_grp > bus->m_dwSizeGetRing) {
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci		/* Ring wraps */
4278c2ecf20Sopenharmony_ci		new_grp -= bus->m_dwSizeGetRing;
4288c2ecf20Sopenharmony_ci		space_rem = bus->m_dwSizeGetRing - curr_grp;
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci		if (space_rem < sizeof(*msg)) {
4318c2ecf20Sopenharmony_ci			if (buf)
4328c2ecf20Sopenharmony_ci				memcpy_fromio(buf, bus->m_pdwGetRing + sizeof(*msg) -
4338c2ecf20Sopenharmony_ci					space_rem, buf_size);
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci		} else if (space_rem == sizeof(*msg)) {
4368c2ecf20Sopenharmony_ci			if (buf)
4378c2ecf20Sopenharmony_ci				memcpy_fromio(buf, bus->m_pdwGetRing, buf_size);
4388c2ecf20Sopenharmony_ci		} else {
4398c2ecf20Sopenharmony_ci			/* Additional data wraps around the ring */
4408c2ecf20Sopenharmony_ci			if (buf) {
4418c2ecf20Sopenharmony_ci				memcpy_fromio(buf, bus->m_pdwGetRing + curr_grp +
4428c2ecf20Sopenharmony_ci					sizeof(*msg), space_rem - sizeof(*msg));
4438c2ecf20Sopenharmony_ci				memcpy_fromio(buf + space_rem - sizeof(*msg),
4448c2ecf20Sopenharmony_ci					bus->m_pdwGetRing, bytes_to_read -
4458c2ecf20Sopenharmony_ci					space_rem);
4468c2ecf20Sopenharmony_ci			}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci		}
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	} else {
4518c2ecf20Sopenharmony_ci		/* No wrapping */
4528c2ecf20Sopenharmony_ci		if (buf)
4538c2ecf20Sopenharmony_ci			memcpy_fromio(buf, bus->m_pdwGetRing + curr_grp + sizeof(*msg),
4548c2ecf20Sopenharmony_ci				buf_size);
4558c2ecf20Sopenharmony_ci	}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	/* Update the read positions, adjusting the ring */
4588c2ecf20Sopenharmony_ci	saa7164_writel(bus->m_dwGetReadPos, new_grp);
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_cipeekout:
4618c2ecf20Sopenharmony_ci	ret = SAA_OK;
4628c2ecf20Sopenharmony_ciout:
4638c2ecf20Sopenharmony_ci	mutex_unlock(&bus->lock);
4648c2ecf20Sopenharmony_ci	saa7164_bus_verify(dev);
4658c2ecf20Sopenharmony_ci	return ret;
4668c2ecf20Sopenharmony_ci}
4678c2ecf20Sopenharmony_ci
468