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