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