162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Driver for the NXP SAA7164 PCIe bridge 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2010-2015 Steven Toth <stoth@kernellabs.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include "saa7164.h" 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci/* The message bus to/from the firmware is a ring buffer in PCI address 1162306a36Sopenharmony_ci * space. Establish the defaults. 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ciint saa7164_bus_setup(struct saa7164_dev *dev) 1462306a36Sopenharmony_ci{ 1562306a36Sopenharmony_ci struct tmComResBusInfo *b = &dev->bus; 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci mutex_init(&b->lock); 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci b->Type = TYPE_BUS_PCIe; 2062306a36Sopenharmony_ci b->m_wMaxReqSize = SAA_DEVICE_MAXREQUESTSIZE; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci b->m_pdwSetRing = (u8 __iomem *)(dev->bmmio + 2362306a36Sopenharmony_ci ((u32)dev->busdesc.CommandRing)); 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci b->m_dwSizeSetRing = SAA_DEVICE_BUFFERBLOCKSIZE; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci b->m_pdwGetRing = (u8 __iomem *)(dev->bmmio + 2862306a36Sopenharmony_ci ((u32)dev->busdesc.ResponseRing)); 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci b->m_dwSizeGetRing = SAA_DEVICE_BUFFERBLOCKSIZE; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci b->m_dwSetWritePos = ((u32)dev->intfdesc.BARLocation) + 3362306a36Sopenharmony_ci (2 * sizeof(u64)); 3462306a36Sopenharmony_ci b->m_dwSetReadPos = b->m_dwSetWritePos + (1 * sizeof(u32)); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci b->m_dwGetWritePos = b->m_dwSetWritePos + (2 * sizeof(u32)); 3762306a36Sopenharmony_ci b->m_dwGetReadPos = b->m_dwSetWritePos + (3 * sizeof(u32)); 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci return 0; 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_civoid saa7164_bus_dump(struct saa7164_dev *dev) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci struct tmComResBusInfo *b = &dev->bus; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci dprintk(DBGLVL_BUS, "Dumping the bus structure:\n"); 4762306a36Sopenharmony_ci dprintk(DBGLVL_BUS, " .type = %d\n", b->Type); 4862306a36Sopenharmony_ci dprintk(DBGLVL_BUS, " .dev->bmmio = 0x%p\n", dev->bmmio); 4962306a36Sopenharmony_ci dprintk(DBGLVL_BUS, " .m_wMaxReqSize = 0x%x\n", b->m_wMaxReqSize); 5062306a36Sopenharmony_ci dprintk(DBGLVL_BUS, " .m_pdwSetRing = 0x%p\n", b->m_pdwSetRing); 5162306a36Sopenharmony_ci dprintk(DBGLVL_BUS, " .m_dwSizeSetRing = 0x%x\n", b->m_dwSizeSetRing); 5262306a36Sopenharmony_ci dprintk(DBGLVL_BUS, " .m_pdwGetRing = 0x%p\n", b->m_pdwGetRing); 5362306a36Sopenharmony_ci dprintk(DBGLVL_BUS, " .m_dwSizeGetRing = 0x%x\n", b->m_dwSizeGetRing); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci dprintk(DBGLVL_BUS, " .m_dwSetReadPos = 0x%x (0x%08x)\n", 5662306a36Sopenharmony_ci b->m_dwSetReadPos, saa7164_readl(b->m_dwSetReadPos)); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci dprintk(DBGLVL_BUS, " .m_dwSetWritePos = 0x%x (0x%08x)\n", 5962306a36Sopenharmony_ci b->m_dwSetWritePos, saa7164_readl(b->m_dwSetWritePos)); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci dprintk(DBGLVL_BUS, " .m_dwGetReadPos = 0x%x (0x%08x)\n", 6262306a36Sopenharmony_ci b->m_dwGetReadPos, saa7164_readl(b->m_dwGetReadPos)); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci dprintk(DBGLVL_BUS, " .m_dwGetWritePos = 0x%x (0x%08x)\n", 6562306a36Sopenharmony_ci b->m_dwGetWritePos, saa7164_readl(b->m_dwGetWritePos)); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* Intensionally throw a BUG() if the state of the message bus looks corrupt */ 7062306a36Sopenharmony_cistatic void saa7164_bus_verify(struct saa7164_dev *dev) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci struct tmComResBusInfo *b = &dev->bus; 7362306a36Sopenharmony_ci int bug = 0; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (saa7164_readl(b->m_dwSetReadPos) > b->m_dwSizeSetRing) 7662306a36Sopenharmony_ci bug++; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (saa7164_readl(b->m_dwSetWritePos) > b->m_dwSizeSetRing) 7962306a36Sopenharmony_ci bug++; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (saa7164_readl(b->m_dwGetReadPos) > b->m_dwSizeGetRing) 8262306a36Sopenharmony_ci bug++; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci if (saa7164_readl(b->m_dwGetWritePos) > b->m_dwSizeGetRing) 8562306a36Sopenharmony_ci bug++; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci if (bug) { 8862306a36Sopenharmony_ci saa_debug = 0xffff; /* Ensure we get the bus dump */ 8962306a36Sopenharmony_ci saa7164_bus_dump(dev); 9062306a36Sopenharmony_ci saa_debug = 1024; /* Ensure we get the bus dump */ 9162306a36Sopenharmony_ci BUG(); 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic void saa7164_bus_dumpmsg(struct saa7164_dev *dev, struct tmComResInfo *m, 9662306a36Sopenharmony_ci void *buf) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci dprintk(DBGLVL_BUS, "Dumping msg structure:\n"); 9962306a36Sopenharmony_ci dprintk(DBGLVL_BUS, " .id = %d\n", m->id); 10062306a36Sopenharmony_ci dprintk(DBGLVL_BUS, " .flags = 0x%x\n", m->flags); 10162306a36Sopenharmony_ci dprintk(DBGLVL_BUS, " .size = 0x%x\n", m->size); 10262306a36Sopenharmony_ci dprintk(DBGLVL_BUS, " .command = 0x%x\n", m->command); 10362306a36Sopenharmony_ci dprintk(DBGLVL_BUS, " .controlselector = 0x%x\n", m->controlselector); 10462306a36Sopenharmony_ci dprintk(DBGLVL_BUS, " .seqno = %d\n", m->seqno); 10562306a36Sopenharmony_ci if (buf) 10662306a36Sopenharmony_ci dprintk(DBGLVL_BUS, " .buffer (ignored)\n"); 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci/* 11062306a36Sopenharmony_ci * Places a command or a response on the bus. The implementation does not 11162306a36Sopenharmony_ci * know if it is a command or a response it just places the data on the 11262306a36Sopenharmony_ci * bus depending on the bus information given in the struct tmComResBusInfo 11362306a36Sopenharmony_ci * structure. If the command or response does not fit into the bus ring 11462306a36Sopenharmony_ci * buffer it will be refused. 11562306a36Sopenharmony_ci * 11662306a36Sopenharmony_ci * Return Value: 11762306a36Sopenharmony_ci * SAA_OK The function executed successfully. 11862306a36Sopenharmony_ci * < 0 One or more members are not initialized. 11962306a36Sopenharmony_ci */ 12062306a36Sopenharmony_ciint saa7164_bus_set(struct saa7164_dev *dev, struct tmComResInfo* msg, 12162306a36Sopenharmony_ci void *buf) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci struct tmComResBusInfo *bus = &dev->bus; 12462306a36Sopenharmony_ci u32 bytes_to_write, free_write_space, timeout, curr_srp, curr_swp; 12562306a36Sopenharmony_ci u32 new_swp, space_rem; 12662306a36Sopenharmony_ci int ret = SAA_ERR_BAD_PARAMETER; 12762306a36Sopenharmony_ci u16 size; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (!msg) { 13062306a36Sopenharmony_ci printk(KERN_ERR "%s() !msg\n", __func__); 13162306a36Sopenharmony_ci return SAA_ERR_BAD_PARAMETER; 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci dprintk(DBGLVL_BUS, "%s()\n", __func__); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci saa7164_bus_verify(dev); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci if (msg->size > dev->bus.m_wMaxReqSize) { 13962306a36Sopenharmony_ci printk(KERN_ERR "%s() Exceeded dev->bus.m_wMaxReqSize\n", 14062306a36Sopenharmony_ci __func__); 14162306a36Sopenharmony_ci return SAA_ERR_BAD_PARAMETER; 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci if ((msg->size > 0) && (buf == NULL)) { 14562306a36Sopenharmony_ci printk(KERN_ERR "%s() Missing message buffer\n", __func__); 14662306a36Sopenharmony_ci return SAA_ERR_BAD_PARAMETER; 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci /* Lock the bus from any other access */ 15062306a36Sopenharmony_ci mutex_lock(&bus->lock); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci bytes_to_write = sizeof(*msg) + msg->size; 15362306a36Sopenharmony_ci free_write_space = 0; 15462306a36Sopenharmony_ci timeout = SAA_BUS_TIMEOUT; 15562306a36Sopenharmony_ci curr_srp = saa7164_readl(bus->m_dwSetReadPos); 15662306a36Sopenharmony_ci curr_swp = saa7164_readl(bus->m_dwSetWritePos); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci /* Deal with ring wrapping issues */ 15962306a36Sopenharmony_ci if (curr_srp > curr_swp) 16062306a36Sopenharmony_ci /* Deal with the wrapped ring */ 16162306a36Sopenharmony_ci free_write_space = curr_srp - curr_swp; 16262306a36Sopenharmony_ci else 16362306a36Sopenharmony_ci /* The ring has not wrapped yet */ 16462306a36Sopenharmony_ci free_write_space = (curr_srp + bus->m_dwSizeSetRing) - curr_swp; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci dprintk(DBGLVL_BUS, "%s() bytes_to_write = %d\n", __func__, 16762306a36Sopenharmony_ci bytes_to_write); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci dprintk(DBGLVL_BUS, "%s() free_write_space = %d\n", __func__, 17062306a36Sopenharmony_ci free_write_space); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci dprintk(DBGLVL_BUS, "%s() curr_srp = %x\n", __func__, curr_srp); 17362306a36Sopenharmony_ci dprintk(DBGLVL_BUS, "%s() curr_swp = %x\n", __func__, curr_swp); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci /* Process the msg and write the content onto the bus */ 17662306a36Sopenharmony_ci while (bytes_to_write >= free_write_space) { 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if (timeout-- == 0) { 17962306a36Sopenharmony_ci printk(KERN_ERR "%s() bus timeout\n", __func__); 18062306a36Sopenharmony_ci ret = SAA_ERR_NO_RESOURCES; 18162306a36Sopenharmony_ci goto out; 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci /* TODO: Review this delay, efficient? */ 18562306a36Sopenharmony_ci /* Wait, allowing the hardware fetch time */ 18662306a36Sopenharmony_ci mdelay(1); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci /* Check the space usage again */ 18962306a36Sopenharmony_ci curr_srp = saa7164_readl(bus->m_dwSetReadPos); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci /* Deal with ring wrapping issues */ 19262306a36Sopenharmony_ci if (curr_srp > curr_swp) 19362306a36Sopenharmony_ci /* Deal with the wrapped ring */ 19462306a36Sopenharmony_ci free_write_space = curr_srp - curr_swp; 19562306a36Sopenharmony_ci else 19662306a36Sopenharmony_ci /* Read didn't wrap around the buffer */ 19762306a36Sopenharmony_ci free_write_space = (curr_srp + bus->m_dwSizeSetRing) - 19862306a36Sopenharmony_ci curr_swp; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci /* Calculate the new write position */ 20362306a36Sopenharmony_ci new_swp = curr_swp + bytes_to_write; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci dprintk(DBGLVL_BUS, "%s() new_swp = %x\n", __func__, new_swp); 20662306a36Sopenharmony_ci dprintk(DBGLVL_BUS, "%s() bus->m_dwSizeSetRing = %x\n", __func__, 20762306a36Sopenharmony_ci bus->m_dwSizeSetRing); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci /* 21062306a36Sopenharmony_ci * Make a copy of msg->size before it is converted to le16 since it is 21162306a36Sopenharmony_ci * used in the code below. 21262306a36Sopenharmony_ci */ 21362306a36Sopenharmony_ci size = msg->size; 21462306a36Sopenharmony_ci /* Convert to le16/le32 */ 21562306a36Sopenharmony_ci msg->size = (__force u16)cpu_to_le16(msg->size); 21662306a36Sopenharmony_ci msg->command = (__force u32)cpu_to_le32(msg->command); 21762306a36Sopenharmony_ci msg->controlselector = (__force u16)cpu_to_le16(msg->controlselector); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci /* Mental Note: line 462 tmmhComResBusPCIe.cpp */ 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci /* Check if we're going to wrap again */ 22262306a36Sopenharmony_ci if (new_swp > bus->m_dwSizeSetRing) { 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci /* Ring wraps */ 22562306a36Sopenharmony_ci new_swp -= bus->m_dwSizeSetRing; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci space_rem = bus->m_dwSizeSetRing - curr_swp; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci dprintk(DBGLVL_BUS, "%s() space_rem = %x\n", __func__, 23062306a36Sopenharmony_ci space_rem); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci dprintk(DBGLVL_BUS, "%s() sizeof(*msg) = %d\n", __func__, 23362306a36Sopenharmony_ci (u32)sizeof(*msg)); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci if (space_rem < sizeof(*msg)) { 23662306a36Sopenharmony_ci dprintk(DBGLVL_BUS, "%s() tr4\n", __func__); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci /* Split the msg into pieces as the ring wraps */ 23962306a36Sopenharmony_ci memcpy_toio(bus->m_pdwSetRing + curr_swp, msg, space_rem); 24062306a36Sopenharmony_ci memcpy_toio(bus->m_pdwSetRing, (u8 *)msg + space_rem, 24162306a36Sopenharmony_ci sizeof(*msg) - space_rem); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci memcpy_toio(bus->m_pdwSetRing + sizeof(*msg) - space_rem, 24462306a36Sopenharmony_ci buf, size); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci } else if (space_rem == sizeof(*msg)) { 24762306a36Sopenharmony_ci dprintk(DBGLVL_BUS, "%s() tr5\n", __func__); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci /* Additional data at the beginning of the ring */ 25062306a36Sopenharmony_ci memcpy_toio(bus->m_pdwSetRing + curr_swp, msg, sizeof(*msg)); 25162306a36Sopenharmony_ci memcpy_toio(bus->m_pdwSetRing, buf, size); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci } else { 25462306a36Sopenharmony_ci /* Additional data wraps around the ring */ 25562306a36Sopenharmony_ci memcpy_toio(bus->m_pdwSetRing + curr_swp, msg, sizeof(*msg)); 25662306a36Sopenharmony_ci if (size > 0) { 25762306a36Sopenharmony_ci memcpy_toio(bus->m_pdwSetRing + curr_swp + 25862306a36Sopenharmony_ci sizeof(*msg), buf, space_rem - 25962306a36Sopenharmony_ci sizeof(*msg)); 26062306a36Sopenharmony_ci memcpy_toio(bus->m_pdwSetRing, (u8 *)buf + 26162306a36Sopenharmony_ci space_rem - sizeof(*msg), 26262306a36Sopenharmony_ci bytes_to_write - space_rem); 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci } /* (new_swp > bus->m_dwSizeSetRing) */ 26862306a36Sopenharmony_ci else { 26962306a36Sopenharmony_ci dprintk(DBGLVL_BUS, "%s() tr6\n", __func__); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* The ring buffer doesn't wrap, two simple copies */ 27262306a36Sopenharmony_ci memcpy_toio(bus->m_pdwSetRing + curr_swp, msg, sizeof(*msg)); 27362306a36Sopenharmony_ci memcpy_toio(bus->m_pdwSetRing + curr_swp + sizeof(*msg), buf, 27462306a36Sopenharmony_ci size); 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci dprintk(DBGLVL_BUS, "%s() new_swp = %x\n", __func__, new_swp); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci /* Update the bus write position */ 28062306a36Sopenharmony_ci saa7164_writel(bus->m_dwSetWritePos, new_swp); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci /* Convert back to cpu after writing the msg to the ringbuffer. */ 28362306a36Sopenharmony_ci msg->size = le16_to_cpu((__force __le16)msg->size); 28462306a36Sopenharmony_ci msg->command = le32_to_cpu((__force __le32)msg->command); 28562306a36Sopenharmony_ci msg->controlselector = le16_to_cpu((__force __le16)msg->controlselector); 28662306a36Sopenharmony_ci ret = SAA_OK; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ciout: 28962306a36Sopenharmony_ci saa7164_bus_dump(dev); 29062306a36Sopenharmony_ci mutex_unlock(&bus->lock); 29162306a36Sopenharmony_ci saa7164_bus_verify(dev); 29262306a36Sopenharmony_ci return ret; 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci/* 29662306a36Sopenharmony_ci * Receive a command or a response from the bus. The implementation does not 29762306a36Sopenharmony_ci * know if it is a command or a response it simply dequeues the data, 29862306a36Sopenharmony_ci * depending on the bus information given in the struct tmComResBusInfo 29962306a36Sopenharmony_ci * structure. 30062306a36Sopenharmony_ci * 30162306a36Sopenharmony_ci * Return Value: 30262306a36Sopenharmony_ci * 0 The function executed successfully. 30362306a36Sopenharmony_ci * < 0 One or more members are not initialized. 30462306a36Sopenharmony_ci */ 30562306a36Sopenharmony_ciint saa7164_bus_get(struct saa7164_dev *dev, struct tmComResInfo* msg, 30662306a36Sopenharmony_ci void *buf, int peekonly) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci struct tmComResBusInfo *bus = &dev->bus; 30962306a36Sopenharmony_ci u32 bytes_to_read, write_distance, curr_grp, curr_gwp, 31062306a36Sopenharmony_ci new_grp, buf_size, space_rem; 31162306a36Sopenharmony_ci struct tmComResInfo msg_tmp; 31262306a36Sopenharmony_ci int ret = SAA_ERR_BAD_PARAMETER; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci saa7164_bus_verify(dev); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci if (msg == NULL) 31762306a36Sopenharmony_ci return ret; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci if (msg->size > dev->bus.m_wMaxReqSize) { 32062306a36Sopenharmony_ci printk(KERN_ERR "%s() Exceeded dev->bus.m_wMaxReqSize\n", 32162306a36Sopenharmony_ci __func__); 32262306a36Sopenharmony_ci return ret; 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci if ((peekonly == 0) && (msg->size > 0) && (buf == NULL)) { 32662306a36Sopenharmony_ci printk(KERN_ERR 32762306a36Sopenharmony_ci "%s() Missing msg buf, size should be %d bytes\n", 32862306a36Sopenharmony_ci __func__, msg->size); 32962306a36Sopenharmony_ci return ret; 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci mutex_lock(&bus->lock); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci /* Peek the bus to see if a msg exists, if it's not what we're expecting 33562306a36Sopenharmony_ci * then return cleanly else read the message from the bus. 33662306a36Sopenharmony_ci */ 33762306a36Sopenharmony_ci curr_gwp = saa7164_readl(bus->m_dwGetWritePos); 33862306a36Sopenharmony_ci curr_grp = saa7164_readl(bus->m_dwGetReadPos); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci if (curr_gwp == curr_grp) { 34162306a36Sopenharmony_ci ret = SAA_ERR_EMPTY; 34262306a36Sopenharmony_ci goto out; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci bytes_to_read = sizeof(*msg); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci /* Calculate write distance to current read position */ 34862306a36Sopenharmony_ci write_distance = 0; 34962306a36Sopenharmony_ci if (curr_gwp >= curr_grp) 35062306a36Sopenharmony_ci /* Write doesn't wrap around the ring */ 35162306a36Sopenharmony_ci write_distance = curr_gwp - curr_grp; 35262306a36Sopenharmony_ci else 35362306a36Sopenharmony_ci /* Write wraps around the ring */ 35462306a36Sopenharmony_ci write_distance = curr_gwp + bus->m_dwSizeGetRing - curr_grp; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci if (bytes_to_read > write_distance) { 35762306a36Sopenharmony_ci printk(KERN_ERR "%s() No message/response found\n", __func__); 35862306a36Sopenharmony_ci ret = SAA_ERR_INVALID_COMMAND; 35962306a36Sopenharmony_ci goto out; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci /* Calculate the new read position */ 36362306a36Sopenharmony_ci new_grp = curr_grp + bytes_to_read; 36462306a36Sopenharmony_ci if (new_grp > bus->m_dwSizeGetRing) { 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci /* Ring wraps */ 36762306a36Sopenharmony_ci new_grp -= bus->m_dwSizeGetRing; 36862306a36Sopenharmony_ci space_rem = bus->m_dwSizeGetRing - curr_grp; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci memcpy_fromio(&msg_tmp, bus->m_pdwGetRing + curr_grp, space_rem); 37162306a36Sopenharmony_ci memcpy_fromio((u8 *)&msg_tmp + space_rem, bus->m_pdwGetRing, 37262306a36Sopenharmony_ci bytes_to_read - space_rem); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci } else { 37562306a36Sopenharmony_ci /* No wrapping */ 37662306a36Sopenharmony_ci memcpy_fromio(&msg_tmp, bus->m_pdwGetRing + curr_grp, bytes_to_read); 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci /* Convert from little endian to CPU */ 37962306a36Sopenharmony_ci msg_tmp.size = le16_to_cpu((__force __le16)msg_tmp.size); 38062306a36Sopenharmony_ci msg_tmp.command = le32_to_cpu((__force __le32)msg_tmp.command); 38162306a36Sopenharmony_ci msg_tmp.controlselector = le16_to_cpu((__force __le16)msg_tmp.controlselector); 38262306a36Sopenharmony_ci memcpy(msg, &msg_tmp, sizeof(*msg)); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci /* No need to update the read positions, because this was a peek */ 38562306a36Sopenharmony_ci /* If the caller specifically want to peek, return */ 38662306a36Sopenharmony_ci if (peekonly) { 38762306a36Sopenharmony_ci goto peekout; 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci /* Check if the command/response matches what is expected */ 39162306a36Sopenharmony_ci if ((msg_tmp.id != msg->id) || (msg_tmp.command != msg->command) || 39262306a36Sopenharmony_ci (msg_tmp.controlselector != msg->controlselector) || 39362306a36Sopenharmony_ci (msg_tmp.seqno != msg->seqno) || (msg_tmp.size != msg->size)) { 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci printk(KERN_ERR "%s() Unexpected msg miss-match\n", __func__); 39662306a36Sopenharmony_ci saa7164_bus_dumpmsg(dev, msg, buf); 39762306a36Sopenharmony_ci saa7164_bus_dumpmsg(dev, &msg_tmp, NULL); 39862306a36Sopenharmony_ci ret = SAA_ERR_INVALID_COMMAND; 39962306a36Sopenharmony_ci goto out; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci /* Get the actual command and response from the bus */ 40362306a36Sopenharmony_ci buf_size = msg->size; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci bytes_to_read = sizeof(*msg) + msg->size; 40662306a36Sopenharmony_ci /* Calculate write distance to current read position */ 40762306a36Sopenharmony_ci write_distance = 0; 40862306a36Sopenharmony_ci if (curr_gwp >= curr_grp) 40962306a36Sopenharmony_ci /* Write doesn't wrap around the ring */ 41062306a36Sopenharmony_ci write_distance = curr_gwp - curr_grp; 41162306a36Sopenharmony_ci else 41262306a36Sopenharmony_ci /* Write wraps around the ring */ 41362306a36Sopenharmony_ci write_distance = curr_gwp + bus->m_dwSizeGetRing - curr_grp; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci if (bytes_to_read > write_distance) { 41662306a36Sopenharmony_ci printk(KERN_ERR "%s() Invalid bus state, missing msg or mangled ring, faulty H/W / bad code?\n", 41762306a36Sopenharmony_ci __func__); 41862306a36Sopenharmony_ci ret = SAA_ERR_INVALID_COMMAND; 41962306a36Sopenharmony_ci goto out; 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci /* Calculate the new read position */ 42362306a36Sopenharmony_ci new_grp = curr_grp + bytes_to_read; 42462306a36Sopenharmony_ci if (new_grp > bus->m_dwSizeGetRing) { 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci /* Ring wraps */ 42762306a36Sopenharmony_ci new_grp -= bus->m_dwSizeGetRing; 42862306a36Sopenharmony_ci space_rem = bus->m_dwSizeGetRing - curr_grp; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci if (space_rem < sizeof(*msg)) { 43162306a36Sopenharmony_ci if (buf) 43262306a36Sopenharmony_ci memcpy_fromio(buf, bus->m_pdwGetRing + sizeof(*msg) - 43362306a36Sopenharmony_ci space_rem, buf_size); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci } else if (space_rem == sizeof(*msg)) { 43662306a36Sopenharmony_ci if (buf) 43762306a36Sopenharmony_ci memcpy_fromio(buf, bus->m_pdwGetRing, buf_size); 43862306a36Sopenharmony_ci } else { 43962306a36Sopenharmony_ci /* Additional data wraps around the ring */ 44062306a36Sopenharmony_ci if (buf) { 44162306a36Sopenharmony_ci memcpy_fromio(buf, bus->m_pdwGetRing + curr_grp + 44262306a36Sopenharmony_ci sizeof(*msg), space_rem - sizeof(*msg)); 44362306a36Sopenharmony_ci memcpy_fromio(buf + space_rem - sizeof(*msg), 44462306a36Sopenharmony_ci bus->m_pdwGetRing, bytes_to_read - 44562306a36Sopenharmony_ci space_rem); 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci } else { 45162306a36Sopenharmony_ci /* No wrapping */ 45262306a36Sopenharmony_ci if (buf) 45362306a36Sopenharmony_ci memcpy_fromio(buf, bus->m_pdwGetRing + curr_grp + sizeof(*msg), 45462306a36Sopenharmony_ci buf_size); 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci /* Update the read positions, adjusting the ring */ 45862306a36Sopenharmony_ci saa7164_writel(bus->m_dwGetReadPos, new_grp); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_cipeekout: 46162306a36Sopenharmony_ci ret = SAA_OK; 46262306a36Sopenharmony_ciout: 46362306a36Sopenharmony_ci mutex_unlock(&bus->lock); 46462306a36Sopenharmony_ci saa7164_bus_verify(dev); 46562306a36Sopenharmony_ci return ret; 46662306a36Sopenharmony_ci} 46762306a36Sopenharmony_ci 468