diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-08-04 20:32:24 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-08-04 20:32:24 -0400 |
commit | 2521129a6d2fd8a81f99cf95055eddea3df914ff (patch) | |
tree | f8b7879979f656669ce31cbc247b97ae702291fb /drivers/misc/mic/host/mic_virtio.c | |
parent | 98a96f202203fecad65b44449077c695686ad4db (diff) | |
parent | 16eb2bfc65ef86d3ac6420d50ddc2c48f0023cee (diff) |
Merge tag 'char-misc-3.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc
Pull char / misc driver patches from Greg KH:
"Here's the big driver misc / char pull request for 3.17-rc1.
Lots of things in here, the thunderbolt support for Apple laptops,
some other new drivers, testing fixes, and other good things. All
have been in linux-next for a long time"
* tag 'char-misc-3.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (119 commits)
misc: bh1780: Introduce the use of devm_kzalloc
Lattice ECP3 FPGA: Correct endianness
drivers/misc/ti-st: Load firmware from ti-connectivity directory.
dt-bindings: extcon: Add support for SM5502 MUIC device
extcon: sm5502: Change internal hardware switch according to cable type
extcon: sm5502: Detect cable state after completing platform booting
extcon: sm5502: Add support new SM5502 extcon device driver
extcon: arizona: Get MICVDD against extcon device
extcon: Remove unnecessary OOM messages
misc: vexpress: Fix sparse non static symbol warnings
mei: drop unused hw dependent fw status functions
misc: bh1770glc: Use managed functions
pcmcia: remove DEFINE_PCI_DEVICE_TABLE usage
misc: remove DEFINE_PCI_DEVICE_TABLE usage
ipack: Replace DEFINE_PCI_DEVICE_TABLE macro use
drivers/char/dsp56k.c: drop check for negativity of unsigned parameter
mei: fix return value on disconnect timeout
mei: don't schedule suspend in pm idle
mei: start disconnect request timer consistently
mei: reset client connection state on timeout
...
Diffstat (limited to 'drivers/misc/mic/host/mic_virtio.c')
-rw-r--r-- | drivers/misc/mic/host/mic_virtio.c | 187 |
1 files changed, 149 insertions, 38 deletions
diff --git a/drivers/misc/mic/host/mic_virtio.c b/drivers/misc/mic/host/mic_virtio.c index 7e1ef0ebbb80..a020e4eb435a 100644 --- a/drivers/misc/mic/host/mic_virtio.c +++ b/drivers/misc/mic/host/mic_virtio.c | |||
@@ -21,60 +21,157 @@ | |||
21 | #include <linux/pci.h> | 21 | #include <linux/pci.h> |
22 | #include <linux/sched.h> | 22 | #include <linux/sched.h> |
23 | #include <linux/uaccess.h> | 23 | #include <linux/uaccess.h> |
24 | 24 | #include <linux/dmaengine.h> | |
25 | #include <linux/mic_common.h> | 25 | #include <linux/mic_common.h> |
26 | |||
26 | #include "../common/mic_dev.h" | 27 | #include "../common/mic_dev.h" |
27 | #include "mic_device.h" | 28 | #include "mic_device.h" |
28 | #include "mic_smpt.h" | 29 | #include "mic_smpt.h" |
29 | #include "mic_virtio.h" | 30 | #include "mic_virtio.h" |
30 | 31 | ||
31 | /* | 32 | /* |
32 | * Initiates the copies across the PCIe bus from card memory to | 33 | * Size of the internal buffer used during DMA's as an intermediate buffer |
33 | * a user space buffer. | 34 | * for copy to/from user. |
34 | */ | 35 | */ |
35 | static int mic_virtio_copy_to_user(struct mic_vdev *mvdev, | 36 | #define MIC_INT_DMA_BUF_SIZE PAGE_ALIGN(64 * 1024ULL) |
36 | void __user *ubuf, size_t len, u64 addr) | 37 | |
38 | static int mic_sync_dma(struct mic_device *mdev, dma_addr_t dst, | ||
39 | dma_addr_t src, size_t len) | ||
37 | { | 40 | { |
38 | int err; | 41 | int err = 0; |
39 | void __iomem *dbuf = mvdev->mdev->aper.va + addr; | 42 | struct dma_async_tx_descriptor *tx; |
40 | /* | 43 | struct dma_chan *mic_ch = mdev->dma_ch; |
41 | * We are copying from IO below an should ideally use something | 44 | |
42 | * like copy_to_user_fromio(..) if it existed. | 45 | if (!mic_ch) { |
43 | */ | 46 | err = -EBUSY; |
44 | if (copy_to_user(ubuf, (void __force *)dbuf, len)) { | 47 | goto error; |
45 | err = -EFAULT; | 48 | } |
46 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | 49 | |
50 | tx = mic_ch->device->device_prep_dma_memcpy(mic_ch, dst, src, len, | ||
51 | DMA_PREP_FENCE); | ||
52 | if (!tx) { | ||
53 | err = -ENOMEM; | ||
54 | goto error; | ||
55 | } else { | ||
56 | dma_cookie_t cookie = tx->tx_submit(tx); | ||
57 | |||
58 | err = dma_submit_error(cookie); | ||
59 | if (err) | ||
60 | goto error; | ||
61 | err = dma_sync_wait(mic_ch, cookie); | ||
62 | } | ||
63 | error: | ||
64 | if (err) | ||
65 | dev_err(mdev->sdev->parent, "%s %d err %d\n", | ||
47 | __func__, __LINE__, err); | 66 | __func__, __LINE__, err); |
48 | goto err; | 67 | return err; |
68 | } | ||
69 | |||
70 | /* | ||
71 | * Initiates the copies across the PCIe bus from card memory to a user | ||
72 | * space buffer. When transfers are done using DMA, source/destination | ||
73 | * addresses and transfer length must follow the alignment requirements of | ||
74 | * the MIC DMA engine. | ||
75 | */ | ||
76 | static int mic_virtio_copy_to_user(struct mic_vdev *mvdev, void __user *ubuf, | ||
77 | size_t len, u64 daddr, size_t dlen, | ||
78 | int vr_idx) | ||
79 | { | ||
80 | struct mic_device *mdev = mvdev->mdev; | ||
81 | void __iomem *dbuf = mdev->aper.va + daddr; | ||
82 | struct mic_vringh *mvr = &mvdev->mvr[vr_idx]; | ||
83 | size_t dma_alignment = 1 << mdev->dma_ch->device->copy_align; | ||
84 | size_t dma_offset; | ||
85 | size_t partlen; | ||
86 | int err; | ||
87 | |||
88 | dma_offset = daddr - round_down(daddr, dma_alignment); | ||
89 | daddr -= dma_offset; | ||
90 | len += dma_offset; | ||
91 | |||
92 | while (len) { | ||
93 | partlen = min_t(size_t, len, MIC_INT_DMA_BUF_SIZE); | ||
94 | |||
95 | err = mic_sync_dma(mdev, mvr->buf_da, daddr, | ||
96 | ALIGN(partlen, dma_alignment)); | ||
97 | if (err) | ||
98 | goto err; | ||
99 | |||
100 | if (copy_to_user(ubuf, mvr->buf + dma_offset, | ||
101 | partlen - dma_offset)) { | ||
102 | err = -EFAULT; | ||
103 | goto err; | ||
104 | } | ||
105 | daddr += partlen; | ||
106 | ubuf += partlen; | ||
107 | dbuf += partlen; | ||
108 | mvdev->in_bytes_dma += partlen; | ||
109 | mvdev->in_bytes += partlen; | ||
110 | len -= partlen; | ||
111 | dma_offset = 0; | ||
49 | } | 112 | } |
50 | mvdev->in_bytes += len; | 113 | return 0; |
51 | err = 0; | ||
52 | err: | 114 | err: |
115 | dev_err(mic_dev(mvdev), "%s %d err %d\n", __func__, __LINE__, err); | ||
53 | return err; | 116 | return err; |
54 | } | 117 | } |
55 | 118 | ||
56 | /* | 119 | /* |
57 | * Initiates copies across the PCIe bus from a user space | 120 | * Initiates copies across the PCIe bus from a user space buffer to card |
58 | * buffer to card memory. | 121 | * memory. When transfers are done using DMA, source/destination addresses |
122 | * and transfer length must follow the alignment requirements of the MIC | ||
123 | * DMA engine. | ||
59 | */ | 124 | */ |
60 | static int mic_virtio_copy_from_user(struct mic_vdev *mvdev, | 125 | static int mic_virtio_copy_from_user(struct mic_vdev *mvdev, void __user *ubuf, |
61 | void __user *ubuf, size_t len, u64 addr) | 126 | size_t len, u64 daddr, size_t dlen, |
127 | int vr_idx) | ||
62 | { | 128 | { |
129 | struct mic_device *mdev = mvdev->mdev; | ||
130 | void __iomem *dbuf = mdev->aper.va + daddr; | ||
131 | struct mic_vringh *mvr = &mvdev->mvr[vr_idx]; | ||
132 | size_t dma_alignment = 1 << mdev->dma_ch->device->copy_align; | ||
133 | size_t partlen; | ||
63 | int err; | 134 | int err; |
64 | void __iomem *dbuf = mvdev->mdev->aper.va + addr; | 135 | |
136 | if (daddr & (dma_alignment - 1)) { | ||
137 | mvdev->tx_dst_unaligned += len; | ||
138 | goto memcpy; | ||
139 | } else if (ALIGN(len, dma_alignment) > dlen) { | ||
140 | mvdev->tx_len_unaligned += len; | ||
141 | goto memcpy; | ||
142 | } | ||
143 | |||
144 | while (len) { | ||
145 | partlen = min_t(size_t, len, MIC_INT_DMA_BUF_SIZE); | ||
146 | |||
147 | if (copy_from_user(mvr->buf, ubuf, partlen)) { | ||
148 | err = -EFAULT; | ||
149 | goto err; | ||
150 | } | ||
151 | err = mic_sync_dma(mdev, daddr, mvr->buf_da, | ||
152 | ALIGN(partlen, dma_alignment)); | ||
153 | if (err) | ||
154 | goto err; | ||
155 | daddr += partlen; | ||
156 | ubuf += partlen; | ||
157 | dbuf += partlen; | ||
158 | mvdev->out_bytes_dma += partlen; | ||
159 | mvdev->out_bytes += partlen; | ||
160 | len -= partlen; | ||
161 | } | ||
162 | memcpy: | ||
65 | /* | 163 | /* |
66 | * We are copying to IO below and should ideally use something | 164 | * We are copying to IO below and should ideally use something |
67 | * like copy_from_user_toio(..) if it existed. | 165 | * like copy_from_user_toio(..) if it existed. |
68 | */ | 166 | */ |
69 | if (copy_from_user((void __force *)dbuf, ubuf, len)) { | 167 | if (copy_from_user((void __force *)dbuf, ubuf, len)) { |
70 | err = -EFAULT; | 168 | err = -EFAULT; |
71 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | ||
72 | __func__, __LINE__, err); | ||
73 | goto err; | 169 | goto err; |
74 | } | 170 | } |
75 | mvdev->out_bytes += len; | 171 | mvdev->out_bytes += len; |
76 | err = 0; | 172 | return 0; |
77 | err: | 173 | err: |
174 | dev_err(mic_dev(mvdev), "%s %d err %d\n", __func__, __LINE__, err); | ||
78 | return err; | 175 | return err; |
79 | } | 176 | } |
80 | 177 | ||
@@ -110,7 +207,8 @@ static inline u32 mic_vringh_iov_consumed(struct vringh_kiov *iov) | |||
110 | * way to override the VRINGH xfer(..) routines as of v3.10. | 207 | * way to override the VRINGH xfer(..) routines as of v3.10. |
111 | */ | 208 | */ |
112 | static int mic_vringh_copy(struct mic_vdev *mvdev, struct vringh_kiov *iov, | 209 | static int mic_vringh_copy(struct mic_vdev *mvdev, struct vringh_kiov *iov, |
113 | void __user *ubuf, size_t len, bool read, size_t *out_len) | 210 | void __user *ubuf, size_t len, bool read, int vr_idx, |
211 | size_t *out_len) | ||
114 | { | 212 | { |
115 | int ret = 0; | 213 | int ret = 0; |
116 | size_t partlen, tot_len = 0; | 214 | size_t partlen, tot_len = 0; |
@@ -118,13 +216,15 @@ static int mic_vringh_copy(struct mic_vdev *mvdev, struct vringh_kiov *iov, | |||
118 | while (len && iov->i < iov->used) { | 216 | while (len && iov->i < iov->used) { |
119 | partlen = min(iov->iov[iov->i].iov_len, len); | 217 | partlen = min(iov->iov[iov->i].iov_len, len); |
120 | if (read) | 218 | if (read) |
121 | ret = mic_virtio_copy_to_user(mvdev, | 219 | ret = mic_virtio_copy_to_user(mvdev, ubuf, partlen, |
122 | ubuf, partlen, | 220 | (u64)iov->iov[iov->i].iov_base, |
123 | (u64)iov->iov[iov->i].iov_base); | 221 | iov->iov[iov->i].iov_len, |
222 | vr_idx); | ||
124 | else | 223 | else |
125 | ret = mic_virtio_copy_from_user(mvdev, | 224 | ret = mic_virtio_copy_from_user(mvdev, ubuf, partlen, |
126 | ubuf, partlen, | 225 | (u64)iov->iov[iov->i].iov_base, |
127 | (u64)iov->iov[iov->i].iov_base); | 226 | iov->iov[iov->i].iov_len, |
227 | vr_idx); | ||
128 | if (ret) { | 228 | if (ret) { |
129 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | 229 | dev_err(mic_dev(mvdev), "%s %d err %d\n", |
130 | __func__, __LINE__, ret); | 230 | __func__, __LINE__, ret); |
@@ -192,8 +292,8 @@ static int _mic_virtio_copy(struct mic_vdev *mvdev, | |||
192 | ubuf = iov.iov_base; | 292 | ubuf = iov.iov_base; |
193 | } | 293 | } |
194 | /* Issue all the read descriptors first */ | 294 | /* Issue all the read descriptors first */ |
195 | ret = mic_vringh_copy(mvdev, riov, ubuf, len, | 295 | ret = mic_vringh_copy(mvdev, riov, ubuf, len, MIC_VRINGH_READ, |
196 | MIC_VRINGH_READ, &out_len); | 296 | copy->vr_idx, &out_len); |
197 | if (ret) { | 297 | if (ret) { |
198 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | 298 | dev_err(mic_dev(mvdev), "%s %d err %d\n", |
199 | __func__, __LINE__, ret); | 299 | __func__, __LINE__, ret); |
@@ -203,8 +303,8 @@ static int _mic_virtio_copy(struct mic_vdev *mvdev, | |||
203 | ubuf += out_len; | 303 | ubuf += out_len; |
204 | copy->out_len += out_len; | 304 | copy->out_len += out_len; |
205 | /* Issue the write descriptors next */ | 305 | /* Issue the write descriptors next */ |
206 | ret = mic_vringh_copy(mvdev, wiov, ubuf, len, | 306 | ret = mic_vringh_copy(mvdev, wiov, ubuf, len, !MIC_VRINGH_READ, |
207 | !MIC_VRINGH_READ, &out_len); | 307 | copy->vr_idx, &out_len); |
208 | if (ret) { | 308 | if (ret) { |
209 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | 309 | dev_err(mic_dev(mvdev), "%s %d err %d\n", |
210 | __func__, __LINE__, ret); | 310 | __func__, __LINE__, ret); |
@@ -589,13 +689,19 @@ int mic_virtio_add_device(struct mic_vdev *mvdev, | |||
589 | dev_dbg(mdev->sdev->parent, | 689 | dev_dbg(mdev->sdev->parent, |
590 | "%s %d index %d va %p info %p vr_size 0x%x\n", | 690 | "%s %d index %d va %p info %p vr_size 0x%x\n", |
591 | __func__, __LINE__, i, vr->va, vr->info, vr_size); | 691 | __func__, __LINE__, i, vr->va, vr->info, vr_size); |
692 | mvr->buf = (void *)__get_free_pages(GFP_KERNEL, | ||
693 | get_order(MIC_INT_DMA_BUF_SIZE)); | ||
694 | mvr->buf_da = mic_map_single(mvdev->mdev, mvr->buf, | ||
695 | MIC_INT_DMA_BUF_SIZE); | ||
592 | } | 696 | } |
593 | 697 | ||
594 | snprintf(irqname, sizeof(irqname), "mic%dvirtio%d", mdev->id, | 698 | snprintf(irqname, sizeof(irqname), "mic%dvirtio%d", mdev->id, |
595 | mvdev->virtio_id); | 699 | mvdev->virtio_id); |
596 | mvdev->virtio_db = mic_next_db(mdev); | 700 | mvdev->virtio_db = mic_next_db(mdev); |
597 | mvdev->virtio_cookie = mic_request_irq(mdev, mic_virtio_intr_handler, | 701 | mvdev->virtio_cookie = mic_request_threaded_irq(mdev, |
598 | irqname, mvdev, mvdev->virtio_db, MIC_INTR_DB); | 702 | mic_virtio_intr_handler, |
703 | NULL, irqname, mvdev, | ||
704 | mvdev->virtio_db, MIC_INTR_DB); | ||
599 | if (IS_ERR(mvdev->virtio_cookie)) { | 705 | if (IS_ERR(mvdev->virtio_cookie)) { |
600 | ret = PTR_ERR(mvdev->virtio_cookie); | 706 | ret = PTR_ERR(mvdev->virtio_cookie); |
601 | dev_dbg(mdev->sdev->parent, "request irq failed\n"); | 707 | dev_dbg(mdev->sdev->parent, "request irq failed\n"); |
@@ -671,6 +777,11 @@ skip_hot_remove: | |||
671 | vqconfig = mic_vq_config(mvdev->dd); | 777 | vqconfig = mic_vq_config(mvdev->dd); |
672 | for (i = 0; i < mvdev->dd->num_vq; i++) { | 778 | for (i = 0; i < mvdev->dd->num_vq; i++) { |
673 | struct mic_vringh *mvr = &mvdev->mvr[i]; | 779 | struct mic_vringh *mvr = &mvdev->mvr[i]; |
780 | |||
781 | mic_unmap_single(mvdev->mdev, mvr->buf_da, | ||
782 | MIC_INT_DMA_BUF_SIZE); | ||
783 | free_pages((unsigned long)mvr->buf, | ||
784 | get_order(MIC_INT_DMA_BUF_SIZE)); | ||
674 | vringh_kiov_cleanup(&mvr->riov); | 785 | vringh_kiov_cleanup(&mvr->riov); |
675 | vringh_kiov_cleanup(&mvr->wiov); | 786 | vringh_kiov_cleanup(&mvr->wiov); |
676 | mic_unmap_single(mdev, le64_to_cpu(vqconfig[i].address), | 787 | mic_unmap_single(mdev, le64_to_cpu(vqconfig[i].address), |