brintos

brintos / linux-shallow public Read only

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