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