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