diff options
| author | Siva Yerramreddy <yshivakrishna@gmail.com> | 2014-07-11 17:04:21 -0400 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-07-12 12:57:42 -0400 |
| commit | 95b4ecbf759ae8ecf40462ed5e6a08023166a05c (patch) | |
| tree | 2f479b7ba4a4678826a85d78f4e90d1baa4f870f | |
| parent | a8438814d3b899e04e808e45585b40931517244f (diff) | |
dma: MIC X100 DMA Driver
This patch implements DMA Engine API for DMA controller on MIC X100
Coprocessors. DMA h/w is shared between host and card s/w.
Channels 0 to 3 are used by host and 4 to 7 are used by card.
Since the DMA device doesn't show up as PCIe device, a virtual bus called mic
bus is created and virtual devices are added on that bus to follow device model.
Allowed dma transfer directions are host to card, card to host and card to card.
Reviewed-by: Ashutosh Dixit <ashutosh.dixit@intel.com>
Reviewed-by: Nikhil Rao <nikhil.rao@intel.com>
Reviewed-by: Sudeep Dutt <sudeep.dutt@intel.com>
Signed-off-by: Siva Yerramreddy <yshivakrishna@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
| -rw-r--r-- | drivers/dma/Kconfig | 19 | ||||
| -rw-r--r-- | drivers/dma/Makefile | 1 | ||||
| -rw-r--r-- | drivers/dma/mic_x100_dma.c | 774 | ||||
| -rw-r--r-- | drivers/dma/mic_x100_dma.h | 286 |
4 files changed, 1080 insertions, 0 deletions
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 1eca7b9760e6..7c8b8c41f4f4 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig | |||
| @@ -33,6 +33,25 @@ if DMADEVICES | |||
| 33 | 33 | ||
| 34 | comment "DMA Devices" | 34 | comment "DMA Devices" |
| 35 | 35 | ||
| 36 | config INTEL_MIC_X100_DMA | ||
| 37 | tristate "Intel MIC X100 DMA Driver" | ||
| 38 | depends on 64BIT && X86 && INTEL_MIC_BUS | ||
| 39 | select DMAENGINE | ||
| 40 | default N | ||
| 41 | help | ||
| 42 | This enables DMA support for the Intel Many Integrated Core | ||
| 43 | (MIC) family of PCIe form factor coprocessor X100 devices that | ||
| 44 | run a 64 bit Linux OS. This driver will be used by both MIC | ||
| 45 | host and card drivers. | ||
| 46 | |||
| 47 | If you are building host kernel with a MIC device or a card | ||
| 48 | kernel for a MIC device, then say M (recommended) or Y, else | ||
| 49 | say N. If unsure say N. | ||
| 50 | |||
| 51 | More information about the Intel MIC family as well as the Linux | ||
| 52 | OS and tools for MIC to use with this driver are available from | ||
| 53 | <http://software.intel.com/en-us/mic-developer>. | ||
| 54 | |||
| 36 | config INTEL_MID_DMAC | 55 | config INTEL_MID_DMAC |
| 37 | tristate "Intel MID DMA support for Peripheral DMA controllers" | 56 | tristate "Intel MID DMA support for Peripheral DMA controllers" |
| 38 | depends on PCI && X86 | 57 | depends on PCI && X86 |
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index c779e1eb2db2..bd9e7fa928bd 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile | |||
| @@ -47,3 +47,4 @@ obj-$(CONFIG_MOXART_DMA) += moxart-dma.o | |||
| 47 | obj-$(CONFIG_FSL_EDMA) += fsl-edma.o | 47 | obj-$(CONFIG_FSL_EDMA) += fsl-edma.o |
| 48 | obj-$(CONFIG_QCOM_BAM_DMA) += qcom_bam_dma.o | 48 | obj-$(CONFIG_QCOM_BAM_DMA) += qcom_bam_dma.o |
| 49 | obj-y += xilinx/ | 49 | obj-y += xilinx/ |
| 50 | obj-$(CONFIG_INTEL_MIC_X100_DMA) += mic_x100_dma.o | ||
diff --git a/drivers/dma/mic_x100_dma.c b/drivers/dma/mic_x100_dma.c new file mode 100644 index 000000000000..6de2e677be04 --- /dev/null +++ b/drivers/dma/mic_x100_dma.c | |||
| @@ -0,0 +1,774 @@ | |||
| 1 | /* | ||
| 2 | * Intel MIC Platform Software Stack (MPSS) | ||
| 3 | * | ||
| 4 | * Copyright(c) 2014 Intel Corporation. | ||
| 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, version 2, as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, but | ||
| 11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * General Public License for more details. | ||
| 14 | * | ||
| 15 | * The full GNU General Public License is included in this distribution in | ||
| 16 | * the file called "COPYING". | ||
| 17 | * | ||
| 18 | * Intel MIC X100 DMA Driver. | ||
| 19 | * | ||
| 20 | * Adapted from IOAT dma driver. | ||
| 21 | */ | ||
| 22 | #include <linux/module.h> | ||
| 23 | #include <linux/io.h> | ||
| 24 | #include <linux/seq_file.h> | ||
| 25 | |||
| 26 | #include "mic_x100_dma.h" | ||
| 27 | |||
| 28 | #define MIC_DMA_MAX_XFER_SIZE_CARD (1 * 1024 * 1024 -\ | ||
| 29 | MIC_DMA_ALIGN_BYTES) | ||
| 30 | #define MIC_DMA_MAX_XFER_SIZE_HOST (1 * 1024 * 1024 >> 1) | ||
| 31 | #define MIC_DMA_DESC_TYPE_SHIFT 60 | ||
| 32 | #define MIC_DMA_MEMCPY_LEN_SHIFT 46 | ||
| 33 | #define MIC_DMA_STAT_INTR_SHIFT 59 | ||
| 34 | |||
| 35 | /* high-water mark for pushing dma descriptors */ | ||
| 36 | static int mic_dma_pending_level = 4; | ||
| 37 | |||
| 38 | /* Status descriptor is used to write a 64 bit value to a memory location */ | ||
| 39 | enum mic_dma_desc_format_type { | ||
| 40 | MIC_DMA_MEMCPY = 1, | ||
| 41 | MIC_DMA_STATUS, | ||
| 42 | }; | ||
| 43 | |||
| 44 | static inline u32 mic_dma_hw_ring_inc(u32 val) | ||
| 45 | { | ||
| 46 | return (val + 1) % MIC_DMA_DESC_RX_SIZE; | ||
| 47 | } | ||
| 48 | |||
| 49 | static inline u32 mic_dma_hw_ring_dec(u32 val) | ||
| 50 | { | ||
| 51 | return val ? val - 1 : MIC_DMA_DESC_RX_SIZE - 1; | ||
| 52 | } | ||
| 53 | |||
| 54 | static inline void mic_dma_hw_ring_inc_head(struct mic_dma_chan *ch) | ||
| 55 | { | ||
| 56 | ch->head = mic_dma_hw_ring_inc(ch->head); | ||
| 57 | } | ||
| 58 | |||
| 59 | /* Prepare a memcpy desc */ | ||
| 60 | static inline void mic_dma_memcpy_desc(struct mic_dma_desc *desc, | ||
| 61 | dma_addr_t src_phys, dma_addr_t dst_phys, u64 size) | ||
| 62 | { | ||
| 63 | u64 qw0, qw1; | ||
| 64 | |||
| 65 | qw0 = src_phys; | ||
| 66 | qw0 |= (size >> MIC_DMA_ALIGN_SHIFT) << MIC_DMA_MEMCPY_LEN_SHIFT; | ||
| 67 | qw1 = MIC_DMA_MEMCPY; | ||
| 68 | qw1 <<= MIC_DMA_DESC_TYPE_SHIFT; | ||
| 69 | qw1 |= dst_phys; | ||
| 70 | desc->qw0 = qw0; | ||
| 71 | desc->qw1 = qw1; | ||
| 72 | } | ||
| 73 | |||
| 74 | /* Prepare a status desc. with @data to be written at @dst_phys */ | ||
| 75 | static inline void mic_dma_prep_status_desc(struct mic_dma_desc *desc, u64 data, | ||
| 76 | dma_addr_t dst_phys, bool generate_intr) | ||
| 77 | { | ||
| 78 | u64 qw0, qw1; | ||
| 79 | |||
| 80 | qw0 = data; | ||
| 81 | qw1 = (u64) MIC_DMA_STATUS << MIC_DMA_DESC_TYPE_SHIFT | dst_phys; | ||
| 82 | if (generate_intr) | ||
| 83 | qw1 |= (1ULL << MIC_DMA_STAT_INTR_SHIFT); | ||
| 84 | desc->qw0 = qw0; | ||
| 85 | desc->qw1 = qw1; | ||
| 86 | } | ||
| 87 | |||
| 88 | static void mic_dma_cleanup(struct mic_dma_chan *ch) | ||
| 89 | { | ||
| 90 | struct dma_async_tx_descriptor *tx; | ||
| 91 | u32 tail; | ||
| 92 | u32 last_tail; | ||
| 93 | |||
| 94 | spin_lock(&ch->cleanup_lock); | ||
| 95 | tail = mic_dma_read_cmp_cnt(ch); | ||
| 96 | /* | ||
| 97 | * This is the barrier pair for smp_wmb() in fn. | ||
| 98 | * mic_dma_tx_submit_unlock. It's required so that we read the | ||
| 99 | * updated cookie value from tx->cookie. | ||
| 100 | */ | ||
| 101 | smp_rmb(); | ||
| 102 | for (last_tail = ch->last_tail; tail != last_tail;) { | ||
| 103 | tx = &ch->tx_array[last_tail]; | ||
| 104 | if (tx->cookie) { | ||
| 105 | dma_cookie_complete(tx); | ||
| 106 | if (tx->callback) { | ||
| 107 | tx->callback(tx->callback_param); | ||
| 108 | tx->callback = NULL; | ||
| 109 | } | ||
| 110 | } | ||
| 111 | last_tail = mic_dma_hw_ring_inc(last_tail); | ||
| 112 | } | ||
| 113 | /* finish all completion callbacks before incrementing tail */ | ||
| 114 | smp_mb(); | ||
| 115 | ch->last_tail = last_tail; | ||
| 116 | spin_unlock(&ch->cleanup_lock); | ||
| 117 | } | ||
| 118 | |||
| 119 | static u32 mic_dma_ring_count(u32 head, u32 tail) | ||
| 120 | { | ||
| 121 | u32 count; | ||
| 122 | |||
| 123 | if (head >= tail) | ||
| 124 | count = (tail - 0) + (MIC_DMA_DESC_RX_SIZE - head); | ||
| 125 | else | ||
| 126 | count = tail - head; | ||
| 127 | return count - 1; | ||
| 128 | } | ||
| 129 | |||
| 130 | /* Returns the num. of free descriptors on success, -ENOMEM on failure */ | ||
| 131 | static int mic_dma_avail_desc_ring_space(struct mic_dma_chan *ch, int required) | ||
| 132 | { | ||
| 133 | struct device *dev = mic_dma_ch_to_device(ch); | ||
| 134 | u32 count; | ||
| 135 | |||
| 136 | count = mic_dma_ring_count(ch->head, ch->last_tail); | ||
| 137 | if (count < required) { | ||
| 138 | mic_dma_cleanup(ch); | ||
| 139 | count = mic_dma_ring_count(ch->head, ch->last_tail); | ||
| 140 | } | ||
| 141 | |||
| 142 | if (count < required) { | ||
| 143 | dev_dbg(dev, "Not enough desc space"); | ||
| 144 | dev_dbg(dev, "%s %d required=%u, avail=%u\n", | ||
| 145 | __func__, __LINE__, required, count); | ||
| 146 | return -ENOMEM; | ||
| 147 | } else { | ||
| 148 | return count; | ||
| 149 | } | ||
| 150 | } | ||
| 151 | |||
| 152 | /* Program memcpy descriptors into the descriptor ring and update s/w head ptr*/ | ||
| 153 | static int mic_dma_prog_memcpy_desc(struct mic_dma_chan *ch, dma_addr_t src, | ||
| 154 | dma_addr_t dst, size_t len) | ||
| 155 | |||
