lkml.org 
[lkml]   [2021]   [Jan]   [8]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH v2 14/34] misc: xlink-pcie: rh: Add core communication logic
    Date
    From: Srikanth Thokala <srikanth.thokala@intel.com>

    Add logic to establish communication with the local host which is through
    ring buffer management and MSI/Doorbell interrupts

    Cc: Arnd Bergmann <arnd@arndb.de>
    Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
    Reviewed-by: Mark Gross <mgross@linux.intel.com>
    Signed-off-by: Srikanth Thokala <srikanth.thokala@intel.com>
    ---
    drivers/misc/xlink-pcie/common/core.h | 11 +-
    drivers/misc/xlink-pcie/remote_host/Makefile | 2 +
    drivers/misc/xlink-pcie/remote_host/core.c | 623 +++++++++++++++++++
    drivers/misc/xlink-pcie/remote_host/pci.c | 48 +-
    4 files changed, 672 insertions(+), 12 deletions(-)
    create mode 100644 drivers/misc/xlink-pcie/remote_host/core.c

    diff --git a/drivers/misc/xlink-pcie/common/core.h b/drivers/misc/xlink-pcie/common/core.h
    index 84985ef41a64..eec8566c19d9 100644
    --- a/drivers/misc/xlink-pcie/common/core.h
    +++ b/drivers/misc/xlink-pcie/common/core.h
    @@ -10,15 +10,11 @@
    #ifndef XPCIE_CORE_HEADER_
    #define XPCIE_CORE_HEADER_

    -#include <linux/io.h>
    -#include <linux/types.h>
    -#include <linux/workqueue.h>
    -#include <linux/slab.h>
    -#include <linux/mutex.h>
    -#include <linux/mempool.h>
    #include <linux/dma-mapping.h>
    -#include <linux/cache.h>
    +#include <linux/mutex.h>
    +#include <linux/slab.h>
    #include <linux/wait.h>
    +#include <linux/workqueue.h>

    #include <linux/xlink_drv_inf.h>

    @@ -64,6 +60,7 @@ struct xpcie_buf_desc {
    struct xpcie_stream {
    size_t frag;
    struct xpcie_pipe pipe;
    + struct xpcie_buf_desc **ddr;
    };

    struct xpcie_list {
    diff --git a/drivers/misc/xlink-pcie/remote_host/Makefile b/drivers/misc/xlink-pcie/remote_host/Makefile
    index 96374a43023e..e8074dbb1161 100644
    --- a/drivers/misc/xlink-pcie/remote_host/Makefile
    +++ b/drivers/misc/xlink-pcie/remote_host/Makefile
    @@ -1,3 +1,5 @@
    obj-$(CONFIG_XLINK_PCIE_RH_DRIVER) += mxlk.o
    mxlk-objs := main.o
    mxlk-objs += pci.o
    +mxlk-objs += core.o
    +mxlk-objs += ../common/util.o
    diff --git a/drivers/misc/xlink-pcie/remote_host/core.c b/drivers/misc/xlink-pcie/remote_host/core.c
    new file mode 100644
    index 000000000000..2e20e1490076
    --- /dev/null
    +++ b/drivers/misc/xlink-pcie/remote_host/core.c
    @@ -0,0 +1,623 @@
    +// SPDX-License-Identifier: GPL-2.0-only
    +/*****************************************************************************
    + *
    + * Intel Keem Bay XLink PCIe Driver
    + *
    + * Copyright (C) 2020 Intel Corporation
    + *
    + ****************************************************************************/
    +
    +#include "pci.h"
    +
    +#include "../common/core.h"
    +#include "../common/util.h"
    +
    +static int intel_xpcie_map_dma(struct xpcie *xpcie, struct xpcie_buf_desc *bd,
    + int direction)
    +{
    + struct xpcie_dev *xdev = container_of(xpcie, struct xpcie_dev, xpcie);
    + struct device *dev = &xdev->pci->dev;
    +
    + bd->phys = dma_map_single(dev, bd->data, bd->length, direction);
    +
    + return dma_mapping_error(dev, bd->phys);
    +}
    +
    +static void intel_xpcie_unmap_dma(struct xpcie *xpcie,
    + struct xpcie_buf_desc *bd,
    + int direction)
    +{
    + struct xpcie_dev *xdev = container_of(xpcie, struct xpcie_dev, xpcie);
    + struct device *dev = &xdev->pci->dev;
    +
    + dma_unmap_single(dev, bd->phys, bd->length, direction);
    +}
    +
    +static void intel_xpcie_txrx_cleanup(struct xpcie *xpcie)
    +{
    + struct xpcie_interface *inf = &xpcie->interfaces[0];
    + struct xpcie_stream *tx = &xpcie->tx;
    + struct xpcie_stream *rx = &xpcie->rx;
    + struct xpcie_buf_desc *bd;
    + int index;
    +
    + xpcie->stop_flag = true;
    + xpcie->no_tx_buffer = false;
    + inf->data_avail = true;
    + wake_up_interruptible(&xpcie->tx_waitq);
    + wake_up_interruptible(&inf->rx_waitq);
    + mutex_lock(&xpcie->wlock);
    + mutex_lock(&inf->rlock);
    +
    + if (tx->ddr) {
    + for (index = 0; index < tx->pipe.ndesc; index++) {
    + struct xpcie_transfer_desc *td = tx->pipe.tdr + index;
    +
    + bd = tx->ddr[index];
    + if (bd) {
    + intel_xpcie_unmap_dma(xpcie, bd, DMA_TO_DEVICE);
    + intel_xpcie_free_tx_bd(xpcie, bd);
    + intel_xpcie_set_td_address(td, 0);
    + intel_xpcie_set_td_length(td, 0);
    + }
    + }
    + kfree(tx->ddr);
    + }
    +
    + if (rx->ddr) {
    + for (index = 0; index < rx->pipe.ndesc; index++) {
    + struct xpcie_transfer_desc *td = rx->pipe.tdr + index;
    +
    + bd = rx->ddr[index];
    + if (bd) {
    + intel_xpcie_unmap_dma(xpcie,
    + bd, DMA_FROM_DEVICE);
    + intel_xpcie_free_rx_bd(xpcie, bd);
    + intel_xpcie_set_td_address(td, 0);
    + intel_xpcie_set_td_length(td, 0);
    + }
    + }
    + kfree(rx->ddr);
    + }
    +
    + intel_xpcie_list_cleanup(&xpcie->tx_pool);
    + intel_xpcie_list_cleanup(&xpcie->rx_pool);
    +
    + mutex_unlock(&inf->rlock);
    + mutex_unlock(&xpcie->wlock);
    +}
    +
    +static int intel_xpcie_txrx_init(struct xpcie *xpcie,
    + struct xpcie_cap_txrx *cap)
    +{
    + struct xpcie_stream *tx = &xpcie->tx;
    + struct xpcie_stream *rx = &xpcie->rx;
    + int tx_pool_size, rx_pool_size;
    + struct xpcie_buf_desc *bd;
    + int rc, index, ndesc;
    +
    + xpcie->txrx = cap;
    + xpcie->fragment_size = intel_xpcie_ioread32(&cap->fragment_size);
    + xpcie->stop_flag = false;
    +
    + tx->pipe.ndesc = intel_xpcie_ioread32(&cap->tx.ndesc);
    + tx->pipe.head = &cap->tx.head;
    + tx->pipe.tail = &cap->tx.tail;
    + tx->pipe.old = intel_xpcie_ioread32(&cap->tx.tail);
    + tx->pipe.tdr = (struct xpcie_transfer_desc *)(xpcie->mmio +
    + intel_xpcie_ioread32(&cap->tx.ring));
    +
    + tx->ddr = kcalloc(tx->pipe.ndesc, sizeof(struct xpcie_buf_desc *),
    + GFP_KERNEL);
    + if (!tx->ddr) {
    + rc = -ENOMEM;
    + goto error;
    + }
    +
    + rx->pipe.ndesc = intel_xpcie_ioread32(&cap->rx.ndesc);
    + rx->pipe.head = &cap->rx.head;
    + rx->pipe.tail = &cap->rx.tail;
    + rx->pipe.old = intel_xpcie_ioread32(&cap->rx.head);
    + rx->pipe.tdr = (struct xpcie_transfer_desc *)(xpcie->mmio +
    + intel_xpcie_ioread32(&cap->rx.ring));
    +
    + rx->ddr = kcalloc(rx->pipe.ndesc, sizeof(struct xpcie_buf_desc *),
    + GFP_KERNEL);
    + if (!rx->ddr) {
    + rc = -ENOMEM;
    + goto error;
    + }
    +
    + intel_xpcie_list_init(&xpcie->rx_pool);
    + rx_pool_size = roundup(SZ_32M, xpcie->fragment_size);
    + ndesc = rx_pool_size / xpcie->fragment_size;
    +
    + for (index = 0; index < ndesc; index++) {
    + bd = intel_xpcie_alloc_bd(xpcie->fragment_size);
    + if (bd) {
    + intel_xpcie_list_put(&xpcie->rx_pool, bd);
    + } else {
    + rc = -ENOMEM;
    + goto error;
    + }
    + }
    +
    + intel_xpcie_list_init(&xpcie->tx_pool);
    + tx_pool_size = roundup(SZ_32M, xpcie->fragment_size);
    + ndesc = tx_pool_size / xpcie->fragment_size;
    +
    + for (index = 0; index < ndesc; index++) {
    + bd = intel_xpcie_alloc_bd(xpcie->fragment_size);
    + if (bd) {
    + intel_xpcie_list_put(&xpcie->tx_pool, bd);
    + } else {
    + rc = -ENOMEM;
    + goto error;
    + }
    + }
    +
    + for (index = 0; index < rx->pipe.ndesc; index++) {
    + struct xpcie_transfer_desc *td = rx->pipe.tdr + index;
    +
    + bd = intel_xpcie_alloc_rx_bd(xpcie);
    + if (!bd) {
    + rc = -ENOMEM;
    + goto error;
    + }
    +
    + if (intel_xpcie_map_dma(xpcie, bd, DMA_FROM_DEVICE)) {
    + dev_err(xpcie_to_dev(xpcie), "failed to map rx bd\n");
    + rc = -ENOMEM;
    + goto error;
    + }
    +
    + rx->ddr[index] = bd;
    + intel_xpcie_set_td_address(td, bd->phys);
    + intel_xpcie_set_td_length(td, bd->length);
    + }
    +
    + return 0;
    +
    +error:
    + intel_xpcie_txrx_cleanup(xpcie);
    +
    + return rc;
    +}
    +
    +static int intel_xpcie_discover_txrx(struct xpcie *xpcie)
    +{
    + struct xpcie_cap_txrx *cap;
    + int error;
    +
    + cap = intel_xpcie_cap_find(xpcie, 0, XPCIE_CAP_TXRX);
    + if (cap)
    + error = intel_xpcie_txrx_init(xpcie, cap);
    + else
    + error = -EIO;
    +
    + return error;
    +}
    +
    +static void intel_xpcie_start_tx(struct xpcie *xpcie, unsigned long delay)
    +{
    + queue_delayed_work(xpcie->tx_wq, &xpcie->tx_event, delay);
    +}
    +
    +static void intel_xpcie_start_rx(struct xpcie *xpcie, unsigned long delay)
    +{
    + queue_delayed_work(xpcie->rx_wq, &xpcie->rx_event, delay);
    +}
    +
    +static void intel_xpcie_rx_event_handler(struct work_struct *work)
    +{
    + struct xpcie *xpcie = container_of(work, struct xpcie, rx_event.work);
    + struct xpcie_dev *xdev = container_of(xpcie, struct xpcie_dev, xpcie);
    + struct xpcie_buf_desc *bd, *replacement = NULL;
    + unsigned long delay = msecs_to_jiffies(1);
    + struct xpcie_stream *rx = &xpcie->rx;
    + struct xpcie_transfer_desc *td;
    + u32 head, tail, ndesc, length;
    + u16 status, interface;
    + int rc;
    +
    + if (intel_xpcie_get_device_status(xpcie) != XPCIE_STATUS_RUN)
    + return;
    +
    + ndesc = rx->pipe.ndesc;
    + tail = intel_xpcie_get_tdr_tail(&rx->pipe);
    + head = intel_xpcie_get_tdr_head(&rx->pipe);
    +
    + while (head != tail) {
    + td = rx->pipe.tdr + head;
    + bd = rx->ddr[head];
    +
    + replacement = intel_xpcie_alloc_rx_bd(xpcie);
    + if (!replacement) {
    + delay = msecs_to_jiffies(20);
    + break;
    + }
    +
    + rc = intel_xpcie_map_dma(xpcie, replacement, DMA_FROM_DEVICE);
    + if (rc) {
    + dev_err(xpcie_to_dev(xpcie),
    + "failed to map rx bd (%d)\n", rc);
    + intel_xpcie_free_rx_bd(xpcie, replacement);
    + break;
    + }
    +
    + status = intel_xpcie_get_td_status(td);
    + interface = intel_xpcie_get_td_interface(td);
    + length = intel_xpcie_get_td_length(td);
    + intel_xpcie_unmap_dma(xpcie, bd, DMA_FROM_DEVICE);
    +
    + if (unlikely(status != XPCIE_DESC_STATUS_SUCCESS) ||
    + unlikely(interface >= XPCIE_NUM_INTERFACES)) {
    + dev_err(xpcie_to_dev(xpcie),
    + "rx desc failure, status(%u), interface(%u)\n",
    + status, interface);
    + intel_xpcie_free_rx_bd(xpcie, bd);
    + } else {
    + bd->interface = interface;
    + bd->length = length;
    + bd->next = NULL;
    +
    + intel_xpcie_add_bd_to_interface(xpcie, bd);
    + }
    +
    + rx->ddr[head] = replacement;
    + intel_xpcie_set_td_address(td, replacement->phys);
    + intel_xpcie_set_td_length(td, replacement->length);
    + head = XPCIE_CIRCULAR_INC(head, ndesc);
    + }
    +
    + if (intel_xpcie_get_tdr_head(&rx->pipe) != head) {
    + intel_xpcie_set_tdr_head(&rx->pipe, head);
    + intel_xpcie_pci_raise_irq(xdev, DATA_RECEIVED, 1);
    + }
    +
    + if (!replacement)
    + intel_xpcie_start_rx(xpcie, delay);
    +}
    +
    +static void intel_xpcie_tx_event_handler(struct work_struct *work)
    +{
    + struct xpcie *xpcie = container_of(work, struct xpcie, tx_event.work);
    + struct xpcie_dev *xdev = container_of(xpcie, struct xpcie_dev, xpcie);
    + struct xpcie_stream *tx = &xpcie->tx;
    + struct xpcie_transfer_desc *td;
    + u32 head, tail, old, ndesc;
    + struct xpcie_buf_desc *bd;
    + size_t bytes, buffers;
    + u16 status;
    +
    + if (intel_xpcie_get_device_status(xpcie) != XPCIE_STATUS_RUN)
    + return;
    +
    + ndesc = tx->pipe.ndesc;
    + old = tx->pipe.old;
    + tail = intel_xpcie_get_tdr_tail(&tx->pipe);
    + head = intel_xpcie_get_tdr_head(&tx->pipe);
    +
    + /* clean old entries first */
    + while (old != head) {
    + bd = tx->ddr[old];
    + td = tx->pipe.tdr + old;
    + status = intel_xpcie_get_td_status(td);
    + if (status != XPCIE_DESC_STATUS_SUCCESS)
    + dev_err(xpcie_to_dev(xpcie),
    + "detected tx desc failure (%u)\n", status);
    +
    + intel_xpcie_unmap_dma(xpcie, bd, DMA_TO_DEVICE);
    + intel_xpcie_free_tx_bd(xpcie, bd);
    + tx->ddr[old] = NULL;
    + old = XPCIE_CIRCULAR_INC(old, ndesc);
    + }
    + tx->pipe.old = old;
    +
    + /* add new entries */
    + while (XPCIE_CIRCULAR_INC(tail, ndesc) != head) {
    + bd = intel_xpcie_list_get(&xpcie->write);
    + if (!bd)
    + break;
    +
    + td = tx->pipe.tdr + tail;
    +
    + if (intel_xpcie_map_dma(xpcie, bd, DMA_TO_DEVICE)) {
    + dev_err(xpcie_to_dev(xpcie),
    + "dma mapping error bd addr %p, size %zu\n",
    + bd->data, bd->length);
    + break;
    + }
    +
    + tx->ddr[tail] = bd;
    + intel_xpcie_set_td_address(td, bd->phys);
    + intel_xpcie_set_td_length(td, bd->length);
    + intel_xpcie_set_td_interface(td, bd->interface);
    + intel_xpcie_set_td_status(td, XPCIE_DESC_STATUS_ERROR);
    +
    + tail = XPCIE_CIRCULAR_INC(tail, ndesc);
    + }
    +
    + if (intel_xpcie_get_tdr_tail(&tx->pipe) != tail) {
    + intel_xpcie_set_tdr_tail(&tx->pipe, tail);
    + intel_xpcie_pci_raise_irq(xdev, DATA_SENT, 1);
    + }
    +
    + intel_xpcie_list_info(&xpcie->write, &bytes, &buffers);
    + if (buffers)
    + xpcie->tx_pending = true;
    + else
    + xpcie->tx_pending = false;
    +}
    +
    +static irqreturn_t intel_xpcie_interrupt(int irq, void *args)
    +{
    + struct xpcie_dev *xdev = args;
    + struct xpcie *xpcie;
    +
    + xpcie = &xdev->xpcie;
    +
    + if (intel_xpcie_get_doorbell(xpcie, FROM_DEVICE, DATA_SENT)) {
    + intel_xpcie_set_doorbell(xpcie, FROM_DEVICE, DATA_SENT, 0);
    + intel_xpcie_start_rx(xpcie, 0);
    + }
    + if (intel_xpcie_get_doorbell(xpcie, FROM_DEVICE, DATA_RECEIVED)) {
    + intel_xpcie_set_doorbell(xpcie, FROM_DEVICE, DATA_RECEIVED, 0);
    + if (xpcie->tx_pending)
    + intel_xpcie_start_tx(xpcie, 0);
    + }
    +
    + return IRQ_HANDLED;
    +}
    +
    +static int intel_xpcie_events_init(struct xpcie *xpcie)
    +{
    + xpcie->rx_wq = alloc_ordered_workqueue(XPCIE_DRIVER_NAME,
    + WQ_MEM_RECLAIM | WQ_HIGHPRI);
    + if (!xpcie->rx_wq) {
    + dev_err(xpcie_to_dev(xpcie), "failed to allocate workqueue\n");
    + return -ENOMEM;
    + }
    +
    + xpcie->tx_wq = alloc_ordered_workqueue(XPCIE_DRIVER_NAME,
    + WQ_MEM_RECLAIM | WQ_HIGHPRI);
    + if (!xpcie->tx_wq) {
    + dev_err(xpcie_to_dev(xpcie), "failed to allocate workqueue\n");
    + destroy_workqueue(xpcie->rx_wq);
    + return -ENOMEM;
    + }
    +
    + INIT_DELAYED_WORK(&xpcie->rx_event, intel_xpcie_rx_event_handler);
    + INIT_DELAYED_WORK(&xpcie->tx_event, intel_xpcie_tx_event_handler);
    +
    + return 0;
    +}
    +
    +static void intel_xpcie_events_cleanup(struct xpcie *xpcie)
    +{
    + cancel_delayed_work_sync(&xpcie->rx_event);
    + cancel_delayed_work_sync(&xpcie->tx_event);
    +
    + destroy_workqueue(xpcie->rx_wq);
    + destroy_workqueue(xpcie->tx_wq);
    +}
    +
    +int intel_xpcie_core_init(struct xpcie *xpcie)
    +{
    + struct xpcie_dev *xdev = container_of(xpcie, struct xpcie_dev, xpcie);
    + int status, rc;
    +
    + status = intel_xpcie_get_device_status(xpcie);
    + if (status != XPCIE_STATUS_RUN) {
    + dev_err(&xdev->pci->dev,
    + "device status not RUNNING (%d)\n", status);
    + rc = -EBUSY;
    + return rc;
    + }
    +
    + if (intel_xpcie_ioread8(xpcie->mmio + XPCIE_MMIO_LEGACY_A0))
    + xpcie->legacy_a0 = true;
    +
    + rc = intel_xpcie_events_init(xpcie);
    + if (rc)
    + return rc;
    +
    + rc = intel_xpcie_discover_txrx(xpcie);
    + if (rc)
    + goto error_txrx;
    +
    + intel_xpcie_interfaces_init(xpcie);
    +
    + rc = intel_xpcie_pci_register_irq(xdev, &intel_xpcie_interrupt);
    + if (rc)
    + goto error_txrx;
    +
    + intel_xpcie_set_host_status(xpcie, XPCIE_STATUS_RUN);
    +
    + return 0;
    +
    +error_txrx:
    + intel_xpcie_events_cleanup(xpcie);
    + intel_xpcie_set_host_status(xpcie, XPCIE_STATUS_ERROR);
    +
    + return rc;
    +}
    +
    +void intel_xpcie_core_cleanup(struct xpcie *xpcie)
    +{
    + if (xpcie->status == XPCIE_STATUS_RUN) {
    + intel_xpcie_set_host_status(xpcie, XPCIE_STATUS_UNINIT);
    + intel_xpcie_events_cleanup(xpcie);
    + intel_xpcie_interfaces_cleanup(xpcie);
    + intel_xpcie_txrx_cleanup(xpcie);
    + }
    +}
    +
    +int intel_xpcie_core_read(struct xpcie *xpcie, void *buffer, size_t *length,
    + uint32_t timeout_ms)
    +{
    + long jiffies_timeout = (long)msecs_to_jiffies(timeout_ms);
    + struct xpcie_interface *inf = &xpcie->interfaces[0];
    + unsigned long jiffies_start = jiffies;
    + struct xpcie_buf_desc *bd;
    + size_t remaining, len;
    + long jiffies_passed = 0;
    + int ret;
    +
    + if (*length == 0)
    + return -EINVAL;
    +
    + if (xpcie->status != XPCIE_STATUS_RUN)
    + return -ENODEV;
    +
    + len = *length;
    + remaining = len;
    + *length = 0;
    +
    + ret = mutex_lock_interruptible(&inf->rlock);
    + if (ret < 0)
    + return -EINTR;
    +
    + do {
    + while (!inf->data_avail) {
    + mutex_unlock(&inf->rlock);
    + if (timeout_ms == 0) {
    + ret = wait_event_interruptible(inf->rx_waitq,
    + inf->data_avail);
    + } else {
    + ret =
    + wait_event_interruptible_timeout(inf->rx_waitq,
    + inf->data_avail,
    + jiffies_timeout -
    + jiffies_passed);
    + if (ret == 0)
    + return -ETIME;
    + }
    + if (ret < 0 || xpcie->stop_flag)
    + return -EINTR;
    +
    + ret = mutex_lock_interruptible(&inf->rlock);
    + if (ret < 0)
    + return -EINTR;
    + }
    +
    + bd = (inf->partial_read) ? inf->partial_read :
    + intel_xpcie_list_get(&inf->read);
    + while (remaining && bd) {
    + size_t bcopy;
    +
    + bcopy = min(remaining, bd->length);
    + memcpy(buffer, bd->data, bcopy);
    +
    + buffer += bcopy;
    + remaining -= bcopy;
    + bd->data += bcopy;
    + bd->length -= bcopy;
    +
    + if (bd->length == 0) {
    + intel_xpcie_free_rx_bd(xpcie, bd);
    + bd = intel_xpcie_list_get(&inf->read);
    + }
    + }
    +
    + /* save for next time */
    + inf->partial_read = bd;
    +
    + if (!bd)
    + inf->data_avail = false;
    +
    + *length = len - remaining;
    +
    + jiffies_passed = (long)jiffies - (long)jiffies_start;
    + } while (remaining > 0 && (jiffies_passed < jiffies_timeout ||
    + timeout_ms == 0));
    +
    + mutex_unlock(&inf->rlock);
    +
    + return 0;
    +}
    +
    +int intel_xpcie_core_write(struct xpcie *xpcie, void *buffer, size_t *length,
    + uint32_t timeout_ms)
    +{
    + long jiffies_timeout = (long)msecs_to_jiffies(timeout_ms);
    + struct xpcie_interface *inf = &xpcie->interfaces[0];
    + unsigned long jiffies_start = jiffies;
    + struct xpcie_buf_desc *bd, *head;
    + long jiffies_passed = 0;
    + size_t remaining, len;
    + int ret;
    +
    + if (*length == 0)
    + return -EINVAL;
    +
    + if (xpcie->status != XPCIE_STATUS_RUN)
    + return -ENODEV;
    +
    + len = *length;
    + remaining = len;
    + *length = 0;
    +
    + ret = mutex_lock_interruptible(&xpcie->wlock);
    + if (ret < 0)
    + return -EINTR;
    +
    + do {
    + bd = intel_xpcie_alloc_tx_bd(xpcie);
    + head = bd;
    + while (!head) {
    + mutex_unlock(&xpcie->wlock);
    + if (timeout_ms == 0) {
    + ret =
    + wait_event_interruptible(xpcie->tx_waitq,
    + !xpcie->no_tx_buffer);
    + } else {
    + ret =
    + wait_event_interruptible_timeout(xpcie->tx_waitq,
    + !xpcie->no_tx_buffer,
    + jiffies_timeout -
    + jiffies_passed);
    + if (ret == 0)
    + return -ETIME;
    + }
    + if (ret < 0 || xpcie->stop_flag)
    + return -EINTR;
    +
    + ret = mutex_lock_interruptible(&xpcie->wlock);
    + if (ret < 0)
    + return -EINTR;
    +
    + bd = intel_xpcie_alloc_tx_bd(xpcie);
    + head = bd;
    + }
    +
    + while (remaining && bd) {
    + size_t bcopy;
    +
    + bcopy = min(bd->length, remaining);
    + memcpy(bd->data, buffer, bcopy);
    +
    + buffer += bcopy;
    + remaining -= bcopy;
    + bd->length = bcopy;
    + bd->interface = inf->id;
    +
    + if (remaining) {
    + bd->next = intel_xpcie_alloc_tx_bd(xpcie);
    + bd = bd->next;
    + }
    + }
    +
    + intel_xpcie_list_put(&xpcie->write, head);
    + intel_xpcie_start_tx(xpcie, 0);
    +
    + *length = len - remaining;
    +
    + jiffies_passed = (long)jiffies - (long)jiffies_start;
    + } while (remaining > 0 && (jiffies_passed < jiffies_timeout ||
    + timeout_ms == 0));
    +
    + mutex_unlock(&xpcie->wlock);
    +
    + return 0;
    +}
    diff --git a/drivers/misc/xlink-pcie/remote_host/pci.c b/drivers/misc/xlink-pcie/remote_host/pci.c
    index 0ca0755b591f..f92a78f41324 100644
    --- a/drivers/misc/xlink-pcie/remote_host/pci.c
    +++ b/drivers/misc/xlink-pcie/remote_host/pci.c
    @@ -208,10 +208,8 @@ static void xpcie_device_poll(struct work_struct *work)
    {
    struct xpcie_dev *xdev = container_of(work, struct xpcie_dev,
    wait_event.work);
    - u32 dev_status = intel_xpcie_ioread32(xdev->xpcie.mmio +
    - XPCIE_MMIO_DEV_STATUS);

    - if (dev_status < XPCIE_STATUS_RUN)
    + if (intel_xpcie_get_device_status(&xdev->xpcie) < XPCIE_STATUS_RUN)
    schedule_delayed_work(&xdev->wait_event,
    msecs_to_jiffies(100));
    else
    @@ -224,9 +222,10 @@ static int intel_xpcie_pci_prepare_dev_reset(struct xpcie_dev *xdev,
    if (mutex_lock_interruptible(&xdev->lock))
    return -EINTR;

    - if (xdev->core_irq_callback)
    + if (xdev->core_irq_callback) {
    xdev->core_irq_callback = NULL;
    -
    + intel_xpcie_core_cleanup(&xdev->xpcie);
    + }
    xdev->xpcie.status = XPCIE_STATUS_OFF;
    if (notify)
    intel_xpcie_pci_raise_irq(xdev, DEV_EVENT, REQUEST_RESET);
    @@ -326,6 +325,8 @@ int intel_xpcie_pci_cleanup(struct xpcie_dev *xdev)
    xdev->core_irq_callback = NULL;
    intel_xpcie_pci_irq_cleanup(xdev);

    + intel_xpcie_core_cleanup(&xdev->xpcie);
    +
    intel_xpcie_pci_unmap_bar(xdev);
    pci_release_regions(xdev->pci);
    pci_disable_device(xdev->pci);
    @@ -359,6 +360,7 @@ int intel_xpcie_pci_raise_irq(struct xpcie_dev *xdev,
    {
    u16 pci_status;

    + intel_xpcie_set_doorbell(&xdev->xpcie, TO_DEVICE, type, value);
    pci_read_config_word(xdev->pci, PCI_STATUS, &pci_status);

    return 0;
    @@ -445,7 +447,43 @@ int intel_xpcie_pci_connect_device(u32 id)
    goto connect_cleanup;
    }

    + rc = intel_xpcie_core_init(&xdev->xpcie);
    + if (rc < 0) {
    + dev_err(&xdev->pci->dev, "failed to sync with device\n");
    + goto connect_cleanup;
    + }
    +
    connect_cleanup:
    mutex_unlock(&xdev->lock);
    return rc;
    }
    +
    +int intel_xpcie_pci_read(u32 id, void *data, size_t *size, u32 timeout)
    +{
    + struct xpcie_dev *xdev = intel_xpcie_get_device_by_id(id);
    +
    + if (!xdev)
    + return -ENODEV;
    +
    + return intel_xpcie_core_read(&xdev->xpcie, data, size, timeout);
    +}
    +
    +int intel_xpcie_pci_write(u32 id, void *data, size_t *size, u32 timeout)
    +{
    + struct xpcie_dev *xdev = intel_xpcie_get_device_by_id(id);
    +
    + if (!xdev)
    + return -ENODEV;
    +
    + return intel_xpcie_core_write(&xdev->xpcie, data, size, timeout);
    +}
    +
    +int intel_xpcie_pci_reset_device(u32 id)
    +{
    + struct xpcie_dev *xdev = intel_xpcie_get_device_by_id(id);
    +
    + if (!xdev)
    + return -ENOMEM;
    +
    + return intel_xpcie_pci_prepare_dev_reset(xdev, true);
    +}
    --
    2.17.1
    \
     
     \ /
      Last update: 2021-01-08 22:31    [W:4.708 / U:1.152 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site