diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-11-06 19:41:06 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-11-06 19:41:06 -0500 |
commit | 1071ec7bc2dabd0a9d12a1ae5570f4fd3ba944ca (patch) | |
tree | 3f889877ae180066a8e682d915680f240fbfd6ec /drivers/misc/mic/host | |
parent | c287322c3aadf45ee15339bffdbc2e9117b9cc7a (diff) | |
parent | 425792266a40189e0b3fec02cb59a69935d8c58c (diff) |
Merge tag 'char-misc-3.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc
Pull char/misc patches from Greg KH:
"Here's the big char/misc driver patchset for 3.13-rc1.
Lots of stuff in here, including some new drivers for Intel's "MIC"
co-processor devices, and a new eeprom driver. Other things include
the driver attribute cleanups, extcon driver updates, hyperv updates,
and a raft of other miscellaneous driver fixes.
All of these have been in linux-next for a while"
* tag 'char-misc-3.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (121 commits)
misc: mic: Fixes for randconfig build errors and warnings.
tifm: fix error return code in tifm_7xx1_probe()
w1-gpio: Use devm_* functions
w1-gpio: Detect of_gpio_error for first gpio
uio: Pass pointers to virt_to_page(), not integers
uio: fix memory leak
misc/at24: avoid infinite loop on write()
misc/93xx46: avoid infinite loop on write()
misc: atmel_pwm: add deferred-probing support
mei: wd: host_init propagate error codes from called functions
mei: replace stray pr_debug with dev_dbg
mei: bus: propagate error code returned by mei_me_cl_by_id
mei: mei_cl_link remove duplicated check for open_handle_count
mei: print correct device state during unexpected reset
mei: nfc: fix memory leak in error path
lkdtm: add tests for additional page permissions
lkdtm: adjust recursion size to avoid warnings
lkdtm: isolate stack corruption test
mei: move host_clients_map cleanup to device init
mei: me: downgrade two errors to debug level
...
Diffstat (limited to 'drivers/misc/mic/host')
-rw-r--r-- | drivers/misc/mic/host/Makefile | 14 | ||||
-rw-r--r-- | drivers/misc/mic/host/mic_boot.c | 300 | ||||
-rw-r--r-- | drivers/misc/mic/host/mic_debugfs.c | 491 | ||||
-rw-r--r-- | drivers/misc/mic/host/mic_device.h | 203 | ||||
-rw-r--r-- | drivers/misc/mic/host/mic_fops.c | 222 | ||||
-rw-r--r-- | drivers/misc/mic/host/mic_fops.h | 32 | ||||
-rw-r--r-- | drivers/misc/mic/host/mic_intr.c | 630 | ||||
-rw-r--r-- | drivers/misc/mic/host/mic_intr.h | 137 | ||||
-rw-r--r-- | drivers/misc/mic/host/mic_main.c | 537 | ||||
-rw-r--r-- | drivers/misc/mic/host/mic_smpt.c | 442 | ||||
-rw-r--r-- | drivers/misc/mic/host/mic_smpt.h | 98 | ||||
-rw-r--r-- | drivers/misc/mic/host/mic_sysfs.c | 459 | ||||
-rw-r--r-- | drivers/misc/mic/host/mic_virtio.c | 700 | ||||
-rw-r--r-- | drivers/misc/mic/host/mic_virtio.h | 138 | ||||
-rw-r--r-- | drivers/misc/mic/host/mic_x100.c | 570 | ||||
-rw-r--r-- | drivers/misc/mic/host/mic_x100.h | 98 |
16 files changed, 5071 insertions, 0 deletions
diff --git a/drivers/misc/mic/host/Makefile b/drivers/misc/mic/host/Makefile new file mode 100644 index 000000000000..c2197f999394 --- /dev/null +++ b/drivers/misc/mic/host/Makefile | |||
@@ -0,0 +1,14 @@ | |||
1 | # | ||
2 | # Makefile - Intel MIC Linux driver. | ||
3 | # Copyright(c) 2013, Intel Corporation. | ||
4 | # | ||
5 | obj-$(CONFIG_INTEL_MIC_HOST) += mic_host.o | ||
6 | mic_host-objs := mic_main.o | ||
7 | mic_host-objs += mic_x100.o | ||
8 | mic_host-objs += mic_sysfs.o | ||
9 | mic_host-objs += mic_smpt.o | ||
10 | mic_host-objs += mic_intr.o | ||
11 | mic_host-objs += mic_boot.o | ||
12 | mic_host-objs += mic_debugfs.o | ||
13 | mic_host-objs += mic_fops.o | ||
14 | mic_host-objs += mic_virtio.o | ||
diff --git a/drivers/misc/mic/host/mic_boot.c b/drivers/misc/mic/host/mic_boot.c new file mode 100644 index 000000000000..b079c65eed6d --- /dev/null +++ b/drivers/misc/mic/host/mic_boot.c | |||
@@ -0,0 +1,300 @@ | |||
1 | /* | ||
2 | * Intel MIC Platform Software Stack (MPSS) | ||
3 | * | ||
4 | * Copyright(c) 2013 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 Host driver. | ||
19 | * | ||
20 | */ | ||
21 | #include <linux/delay.h> | ||
22 | #include <linux/firmware.h> | ||
23 | #include <linux/pci.h> | ||
24 | |||
25 | #include <linux/mic_common.h> | ||
26 | #include "../common/mic_dev.h" | ||
27 | #include "mic_device.h" | ||
28 | #include "mic_smpt.h" | ||
29 | #include "mic_virtio.h" | ||
30 | |||
31 | /** | ||
32 | * mic_reset - Reset the MIC device. | ||
33 | * @mdev: pointer to mic_device instance | ||
34 | */ | ||
35 | static void mic_reset(struct mic_device *mdev) | ||
36 | { | ||
37 | int i; | ||
38 | |||
39 | #define MIC_RESET_TO (45) | ||
40 | |||
41 | INIT_COMPLETION(mdev->reset_wait); | ||
42 | mdev->ops->reset_fw_ready(mdev); | ||
43 | mdev->ops->reset(mdev); | ||
44 | |||
45 | for (i = 0; i < MIC_RESET_TO; i++) { | ||
46 | if (mdev->ops->is_fw_ready(mdev)) | ||
47 | goto done; | ||
48 | /* | ||
49 | * Resets typically take 10s of seconds to complete. | ||
50 | * Since an MMIO read is required to check if the | ||
51 | * firmware is ready or not, a 1 second delay works nicely. | ||
52 | */ | ||
53 | msleep(1000); | ||
54 | } | ||
55 | mic_set_state(mdev, MIC_RESET_FAILED); | ||
56 | done: | ||
57 | complete_all(&mdev->reset_wait); | ||
58 | } | ||
59 | |||
60 | /* Initialize the MIC bootparams */ | ||
61 | void mic_bootparam_init(struct mic_device *mdev) | ||
62 | { | ||
63 | struct mic_bootparam *bootparam = mdev->dp; | ||
64 | |||
65 | bootparam->magic = MIC_MAGIC; | ||
66 | bootparam->c2h_shutdown_db = mdev->shutdown_db; | ||
67 | bootparam->h2c_shutdown_db = -1; | ||
68 | bootparam->h2c_config_db = -1; | ||
69 | bootparam->shutdown_status = 0; | ||
70 | bootparam->shutdown_card = 0; | ||
71 | } | ||
72 | |||
73 | /** | ||
74 | * mic_start - Start the MIC. | ||
75 | * @mdev: pointer to mic_device instance | ||
76 | * @buf: buffer containing boot string including firmware/ramdisk path. | ||
77 | * | ||
78 | * This function prepares an MIC for boot and initiates boot. | ||
79 | * RETURNS: An appropriate -ERRNO error value on error, or zero for success. | ||
80 | */ | ||
81 | int mic_start(struct mic_device *mdev, const char *buf) | ||
82 | { | ||
83 | int rc; | ||
84 | mutex_lock(&mdev->mic_mutex); | ||
85 | retry: | ||
86 | if (MIC_OFFLINE != mdev->state) { | ||
87 | rc = -EINVAL; | ||
88 | goto unlock_ret; | ||
89 | } | ||
90 | if (!mdev->ops->is_fw_ready(mdev)) { | ||
91 | mic_reset(mdev); | ||
92 | /* | ||
93 | * The state will either be MIC_OFFLINE if the reset succeeded | ||
94 | * or MIC_RESET_FAILED if the firmware reset failed. | ||
95 | */ | ||
96 | goto retry; | ||
97 | } | ||
98 | rc = mdev->ops->load_mic_fw(mdev, buf); | ||
99 | if (rc) | ||
100 | goto unlock_ret; | ||
101 | mic_smpt_restore(mdev); | ||
102 | mic_intr_restore(mdev); | ||
103 | mdev->intr_ops->enable_interrupts(mdev); | ||
104 | mdev->ops->write_spad(mdev, MIC_DPLO_SPAD, mdev->dp_dma_addr); | ||
105 | mdev->ops->write_spad(mdev, MIC_DPHI_SPAD, mdev->dp_dma_addr >> 32); | ||
106 | mdev->ops->send_firmware_intr(mdev); | ||
107 | mic_set_state(mdev, MIC_ONLINE); | ||
108 | unlock_ret: | ||
109 | mutex_unlock(&mdev->mic_mutex); | ||
110 | return rc; | ||
111 | } | ||
112 | |||
113 | /** | ||
114 | * mic_stop - Prepare the MIC for reset and trigger reset. | ||
115 | * @mdev: pointer to mic_device instance | ||
116 | * @force: force a MIC to reset even if it is already offline. | ||
117 | * | ||
118 | * RETURNS: None. | ||
119 | */ | ||
120 | void mic_stop(struct mic_device *mdev, bool force) | ||
121 | { | ||
122 | mutex_lock(&mdev->mic_mutex); | ||
123 | if (MIC_OFFLINE != mdev->state || force) { | ||
124 | mic_virtio_reset_devices(mdev); | ||
125 | mic_bootparam_init(mdev); | ||
126 | mic_reset(mdev); | ||
127 | if (MIC_RESET_FAILED == mdev->state) | ||
128 | goto unlock; | ||
129 | mic_set_shutdown_status(mdev, MIC_NOP); | ||
130 | if (MIC_SUSPENDED != mdev->state) | ||
131 | mic_set_state(mdev, MIC_OFFLINE); | ||
132 | } | ||
133 | unlock: | ||
134 | mutex_unlock(&mdev->mic_mutex); | ||
135 | } | ||
136 | |||
137 | /** | ||
138 | * mic_shutdown - Initiate MIC shutdown. | ||
139 | * @mdev: pointer to mic_device instance | ||
140 | * | ||
141 | * RETURNS: None. | ||
142 | */ | ||
143 | void mic_shutdown(struct mic_device *mdev) | ||
144 | { | ||
145 | struct mic_bootparam *bootparam = mdev->dp; | ||
146 | s8 db = bootparam->h2c_shutdown_db; | ||
147 | |||
148 | mutex_lock(&mdev->mic_mutex); | ||
149 | if (MIC_ONLINE == mdev->state && db != -1) { | ||
150 | bootparam->shutdown_card = 1; | ||
151 | mdev->ops->send_intr(mdev, db); | ||
152 | mic_set_state(mdev, MIC_SHUTTING_DOWN); | ||
153 | } | ||
154 | mutex_unlock(&mdev->mic_mutex); | ||
155 | } | ||
156 | |||
157 | /** | ||
158 | * mic_shutdown_work - Handle shutdown interrupt from MIC. | ||
159 | * @work: The work structure. | ||
160 | * | ||
161 | * This work is scheduled whenever the host has received a shutdown | ||
162 | * interrupt from the MIC. | ||
163 | */ | ||
164 | void mic_shutdown_work(struct work_struct *work) | ||
165 | { | ||
166 | struct mic_device *mdev = container_of(work, struct mic_device, | ||
167 | shutdown_work); | ||
168 | struct mic_bootparam *bootparam = mdev->dp; | ||
169 | |||
170 | mutex_lock(&mdev->mic_mutex); | ||
171 | mic_set_shutdown_status(mdev, bootparam->shutdown_status); | ||
172 | bootparam->shutdown_status = 0; | ||
173 | |||
174 | /* | ||
175 | * if state is MIC_SUSPENDED, OSPM suspend is in progress. We do not | ||
176 | * change the state here so as to prevent users from booting the card | ||
177 | * during and after the suspend operation. | ||
178 | */ | ||
179 | if (MIC_SHUTTING_DOWN != mdev->state && | ||
180 | MIC_SUSPENDED != mdev->state) | ||
181 | mic_set_state(mdev, MIC_SHUTTING_DOWN); | ||
182 | mutex_unlock(&mdev->mic_mutex); | ||
183 | } | ||
184 | |||
185 | /** | ||
186 | * mic_reset_trigger_work - Trigger MIC reset. | ||
187 | * @work: The work structure. | ||
188 | * | ||
189 | * This work is scheduled whenever the host wants to reset the MIC. | ||
190 | */ | ||
191 | void mic_reset_trigger_work(struct work_struct *work) | ||
192 | { | ||
193 | struct mic_device *mdev = container_of(work, struct mic_device, | ||
194 | reset_trigger_work); | ||
195 | |||
196 | mic_stop(mdev, false); | ||
197 | } | ||
198 | |||
199 | /** | ||
200 | * mic_complete_resume - Complete MIC Resume after an OSPM suspend/hibernate | ||
201 | * event. | ||
202 | * @mdev: pointer to mic_device instance | ||
203 | * | ||
204 | * RETURNS: None. | ||
205 | */ | ||
206 | void mic_complete_resume(struct mic_device *mdev) | ||
207 | { | ||
208 | if (mdev->state != MIC_SUSPENDED) { | ||
209 | dev_warn(mdev->sdev->parent, "state %d should be %d\n", | ||
210 | mdev->state, MIC_SUSPENDED); | ||
211 | return; | ||
212 | } | ||
213 | |||
214 | /* Make sure firmware is ready */ | ||
215 | if (!mdev->ops->is_fw_ready(mdev)) | ||
216 | mic_stop(mdev, true); | ||
217 | |||
218 | mutex_lock(&mdev->mic_mutex); | ||
219 | mic_set_state(mdev, MIC_OFFLINE); | ||
220 | mutex_unlock(&mdev->mic_mutex); | ||
221 | } | ||
222 | |||
223 | /** | ||
224 | * mic_prepare_suspend - Handle suspend notification for the MIC device. | ||
225 | * @mdev: pointer to mic_device instance | ||
226 | * | ||
227 | * RETURNS: None. | ||
228 | */ | ||
229 | void mic_prepare_suspend(struct mic_device *mdev) | ||
230 | { | ||
231 | int rc; | ||
232 | |||
233 | #define MIC_SUSPEND_TIMEOUT (60 * HZ) | ||
234 | |||
235 | mutex_lock(&mdev->mic_mutex); | ||
236 | switch (mdev->state) { | ||
237 | case MIC_OFFLINE: | ||
238 | /* | ||
239 | * Card is already offline. Set state to MIC_SUSPENDED | ||
240 | * to prevent users from booting the card. | ||
241 | */ | ||
242 | mic_set_state(mdev, MIC_SUSPENDED); | ||
243 | mutex_unlock(&mdev->mic_mutex); | ||
244 | break; | ||
245 | case MIC_ONLINE: | ||
246 | /* | ||
247 | * Card is online. Set state to MIC_SUSPENDING and notify | ||
248 | * MIC user space daemon which will issue card | ||
249 | * shutdown and reset. | ||
250 | */ | ||
251 | mic_set_state(mdev, MIC_SUSPENDING); | ||
252 | mutex_unlock(&mdev->mic_mutex); | ||
253 | rc = wait_for_completion_timeout(&mdev->reset_wait, | ||
254 | MIC_SUSPEND_TIMEOUT); | ||
255 | /* Force reset the card if the shutdown completion timed out */ | ||
256 | if (!rc) { | ||
257 | mutex_lock(&mdev->mic_mutex); | ||
258 | mic_set_state(mdev, MIC_SUSPENDED); | ||
259 | mutex_unlock(&mdev->mic_mutex); | ||
260 | mic_stop(mdev, true); | ||
261 | } | ||
262 | break; | ||
263 | case MIC_SHUTTING_DOWN: | ||
264 | /* | ||
265 | * Card is shutting down. Set state to MIC_SUSPENDED | ||
266 | * to prevent further boot of the card. | ||
267 | */ | ||
268 | mic_set_state(mdev, MIC_SUSPENDED); | ||
269 | mutex_unlock(&mdev->mic_mutex); | ||
270 | rc = wait_for_completion_timeout(&mdev->reset_wait, | ||
271 | MIC_SUSPEND_TIMEOUT); | ||
272 | /* Force reset the card if the shutdown completion timed out */ | ||
273 | if (!rc) | ||
274 | mic_stop(mdev, true); | ||
275 | break; | ||
276 | default: | ||
277 | mutex_unlock(&mdev->mic_mutex); | ||
278 | break; | ||
279 | } | ||
280 | } | ||
281 | |||
282 | /** | ||
283 | * mic_suspend - Initiate MIC suspend. Suspend merely issues card shutdown. | ||
284 | * @mdev: pointer to mic_device instance | ||
285 | * | ||
286 | * RETURNS: None. | ||
287 | */ | ||
288 | void mic_suspend(struct mic_device *mdev) | ||
289 | { | ||
290 | struct mic_bootparam *bootparam = mdev->dp; | ||
291 | s8 db = bootparam->h2c_shutdown_db; | ||
292 | |||
293 | mutex_lock(&mdev->mic_mutex); | ||
294 | if (MIC_SUSPENDING == mdev->state && db != -1) { | ||
295 | bootparam->shutdown_card = 1; | ||
296 | mdev->ops->send_intr(mdev, db); | ||
297 | mic_set_state(mdev, MIC_SUSPENDED); | ||
298 | } | ||
299 | mutex_unlock(&mdev->mic_mutex); | ||
300 | } | ||
diff --git a/drivers/misc/mic/host/mic_debugfs.c b/drivers/misc/mic/host/mic_debugfs.c new file mode 100644 index 000000000000..028ba5d6fd1c --- /dev/null +++ b/drivers/misc/mic/host/mic_debugfs.c | |||
@@ -0,0 +1,491 @@ | |||
1 | /* | ||
2 | * Intel MIC Platform Software Stack (MPSS) | ||
3 | * | ||
4 | * Copyright(c) 2013 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 Host driver. | ||
19 | * | ||
20 | */ | ||
21 | #include <linux/debugfs.h> | ||
22 | #include <linux/pci.h> | ||
23 | #include <linux/seq_file.h> | ||
24 | |||
25 | #include <linux/mic_common.h> | ||
26 | #include "../common/mic_dev.h" | ||
27 | #include "mic_device.h" | ||
28 | #include "mic_smpt.h" | ||
29 | #include "mic_virtio.h" | ||
30 | |||
31 | /* Debugfs parent dir */ | ||
32 | static struct dentry *mic_dbg; | ||
33 | |||
34 | /** | ||
35 | * mic_log_buf_show - Display MIC kernel log buffer. | ||
36 | * | ||
37 | * log_buf addr/len is read from System.map by user space | ||
38 | * and populated in sysfs entries. | ||
39 | */ | ||
40 | static int mic_log_buf_show(struct seq_file *s, void *unused) | ||
41 | { | ||
42 | void __iomem *log_buf_va; | ||
43 | int __iomem *log_buf_len_va; | ||
44 | struct mic_device *mdev = s->private; | ||
45 | void *kva; | ||
46 | int size; | ||
47 | unsigned long aper_offset; | ||
48 | |||
49 | if (!mdev || !mdev->log_buf_addr || !mdev->log_buf_len) | ||
50 | goto done; | ||
51 | /* | ||
52 | * Card kernel will never be relocated and any kernel text/data mapping | ||
53 | * can be translated to phys address by subtracting __START_KERNEL_map. | ||
54 | */ | ||
55 | aper_offset = (unsigned long)mdev->log_buf_len - __START_KERNEL_map; | ||
56 | log_buf_len_va = mdev->aper.va + aper_offset; | ||
57 | aper_offset = (unsigned long)mdev->log_buf_addr - __START_KERNEL_map; | ||
58 | log_buf_va = mdev->aper.va + aper_offset; | ||
59 | size = ioread32(log_buf_len_va); | ||
60 | |||
61 | kva = kmalloc(size, GFP_KERNEL); | ||
62 | if (!kva) | ||
63 | goto done; | ||
64 | mutex_lock(&mdev->mic_mutex); | ||
65 | memcpy_fromio(kva, log_buf_va, size); | ||
66 | switch (mdev->state) { | ||
67 | case MIC_ONLINE: | ||
68 | /* Fall through */ | ||
69 | case MIC_SHUTTING_DOWN: | ||
70 | seq_write(s, kva, size); | ||
71 | break; | ||
72 | default: | ||
73 | break; | ||
74 | } | ||
75 | mutex_unlock(&mdev->mic_mutex); | ||
76 | kfree(kva); | ||
77 | done: | ||
78 | return 0; | ||
79 | } | ||
80 | |||
81 | static int mic_log_buf_open(struct inode *inode, struct file *file) | ||
82 | { | ||
83 | return single_open(file, mic_log_buf_show, inode->i_private); | ||
84 | } | ||
85 | |||
86 | static int mic_log_buf_release(struct inode *inode, struct file *file) | ||
87 | { | ||
88 | return single_release(inode, file); | ||
89 | } | ||
90 | |||
91 | static const struct file_operations log_buf_ops = { | ||
92 | .owner = THIS_MODULE, | ||
93 | .open = mic_log_buf_open, | ||
94 | .read = seq_read, | ||
95 | .llseek = seq_lseek, | ||
96 | .release = mic_log_buf_release | ||
97 | }; | ||
98 | |||
99 | static int mic_smpt_show(struct seq_file *s, void *pos) | ||
100 | { | ||
101 | int i; | ||
102 | struct mic_device *mdev = s->private; | ||
103 | unsigned long flags; | ||
104 | |||
105 | seq_printf(s, "MIC %-2d |%-10s| %-14s %-10s\n", | ||
106 | mdev->id, "SMPT entry", "SW DMA addr", "RefCount"); | ||
107 | seq_puts(s, "====================================================\n"); | ||
108 | |||
109 | if (mdev->smpt) { | ||
110 | struct mic_smpt_info *smpt_info = mdev->smpt; | ||
111 | spin_lock_irqsave(&smpt_info->smpt_lock, flags); | ||
112 | for (i = 0; i < smpt_info->info.num_reg; i++) { | ||
113 | seq_printf(s, "%9s|%-10d| %-#14llx %-10lld\n", | ||
114 | " ", i, smpt_info->entry[i].dma_addr, | ||
115 | smpt_info->entry[i].ref_count); | ||
116 | } | ||
117 | spin_unlock_irqrestore(&smpt_info->smpt_lock, flags); | ||
118 | } | ||
119 | seq_puts(s, "====================================================\n"); | ||
120 | return 0; | ||
121 | } | ||
122 | |||
123 | static int mic_smpt_debug_open(struct inode *inode, struct file *file) | ||
124 | { | ||
125 | return single_open(file, mic_smpt_show, inode->i_private); | ||
126 | } | ||
127 | |||
128 | static int mic_smpt_debug_release(struct inode *inode, struct file *file) | ||
129 | { | ||
130 | return single_release(inode, file); | ||
131 | } | ||
132 | |||
133 | static const struct file_operations smpt_file_ops = { | ||
134 | .owner = THIS_MODULE, | ||
135 | .open = mic_smpt_debug_open, | ||
136 | .read = seq_read, | ||
137 | .llseek = seq_lseek, | ||
138 | .release = mic_smpt_debug_release | ||
139 | }; | ||
140 | |||
141 | static int mic_soft_reset_show(struct seq_file *s, void *pos) | ||
142 | { | ||
143 | struct mic_device *mdev = s->private; | ||
144 | |||
145 | mic_stop(mdev, true); | ||
146 | return 0; | ||
147 | } | ||
148 | |||
149 | static int mic_soft_reset_debug_open(struct inode *inode, struct file *file) | ||
150 | { | ||
151 | return single_open(file, mic_soft_reset_show, inode->i_private); | ||
152 | } | ||
153 | |||
154 | static int mic_soft_reset_debug_release(struct inode *inode, struct file *file) | ||
155 | { | ||
156 | return single_release(inode, file); | ||
157 | } | ||
158 | |||
159 | static const struct file_operations soft_reset_ops = { | ||
160 | .owner = THIS_MODULE, | ||
161 | .open = mic_soft_reset_debug_open, | ||
162 | .read = seq_read, | ||
163 | .llseek = seq_lseek, | ||
164 | .release = mic_soft_reset_debug_release | ||
165 | }; | ||
166 | |||
167 | static int mic_post_code_show(struct seq_file *s, void *pos) | ||
168 | { | ||
169 | struct mic_device *mdev = s->private; | ||
170 | u32 reg = mdev->ops->get_postcode(mdev); | ||
171 | |||
172 | seq_printf(s, "%c%c", reg & 0xff, (reg >> 8) & 0xff); | ||
173 | return 0; | ||
174 | } | ||
175 | |||
176 | static int mic_post_code_debug_open(struct inode *inode, struct file *file) | ||
177 | { | ||
178 | return single_open(file, mic_post_code_show, inode->i_private); | ||
179 | } | ||
180 | |||
181 | static int mic_post_code_debug_release(struct inode *inode, struct file *file) | ||
182 | { | ||
183 | return single_release(inode, file); | ||
184 | } | ||
185 | |||
186 | static const struct file_operations post_code_ops = { | ||
187 | .owner = THIS_MODULE, | ||
188 | .open = mic_post_code_debug_open, | ||
189 | .read = seq_read, | ||
190 | .llseek = seq_lseek, | ||
191 | .release = mic_post_code_debug_release | ||
192 | }; | ||
193 | |||
194 | static int mic_dp_show(struct seq_file *s, void *pos) | ||
195 | { | ||
196 | struct mic_device *mdev = s->private; | ||
197 | struct mic_device_desc *d; | ||
198 | struct mic_device_ctrl *dc; | ||
199 | struct mic_vqconfig *vqconfig; | ||
200 | __u32 *features; | ||
201 | __u8 *config; | ||
202 | struct mic_bootparam *bootparam = mdev->dp; | ||
203 | int i, j; | ||
204 | |||
205 | seq_printf(s, "Bootparam: magic 0x%x\n", | ||
206 | bootparam->magic); | ||
207 | seq_printf(s, "Bootparam: h2c_shutdown_db %d\n", | ||
208 | bootparam->h2c_shutdown_db); | ||
209 | seq_printf(s, "Bootparam: h2c_config_db %d\n", | ||
210 | bootparam->h2c_config_db); | ||
211 | seq_printf(s, "Bootparam: c2h_shutdown_db %d\n", | ||
212 | bootparam->c2h_shutdown_db); | ||
213 | seq_printf(s, "Bootparam: shutdown_status %d\n", | ||
214 | bootparam->shutdown_status); | ||
215 | seq_printf(s, "Bootparam: shutdown_card %d\n", | ||
216 | bootparam->shutdown_card); | ||
217 | |||
218 | for (i = sizeof(*bootparam); i < MIC_DP_SIZE; | ||
219 | i += mic_total_desc_size(d)) { | ||
220 | d = mdev->dp + i; | ||
221 | dc = (void *)d + mic_aligned_desc_size(d); | ||
222 | |||
223 | /* end of list */ | ||
224 | if (d->type == 0) | ||
225 | break; | ||
226 | |||
227 | if (d->type == -1) | ||
228 | continue; | ||
229 | |||
230 | seq_printf(s, "Type %d ", d->type); | ||
231 | seq_printf(s, "Num VQ %d ", d->num_vq); | ||
232 | seq_printf(s, "Feature Len %d\n", d->feature_len); | ||
233 | seq_printf(s, "Config Len %d ", d->config_len); | ||
234 | seq_printf(s, "Shutdown Status %d\n", d->status); | ||
235 | |||
236 | for (j = 0; j < d->num_vq; j++) { | ||
237 | vqconfig = mic_vq_config(d) + j; | ||
238 | seq_printf(s, "vqconfig[%d]: ", j); | ||
239 | seq_printf(s, "address 0x%llx ", vqconfig->address); | ||
240 | seq_printf(s, "num %d ", vqconfig->num); | ||
241 | seq_printf(s, "used address 0x%llx\n", | ||
242 | vqconfig->used_address); | ||
243 | } | ||
244 | |||
245 | features = (__u32 *)mic_vq_features(d); | ||
246 | seq_printf(s, "Features: Host 0x%x ", features[0]); | ||
247 | seq_printf(s, "Guest 0x%x\n", features[1]); | ||
248 | |||
249 | config = mic_vq_configspace(d); | ||
250 | for (j = 0; j < d->config_len; j++) | ||
251 | seq_printf(s, "config[%d]=%d\n", j, config[j]); | ||
252 | |||
253 | seq_puts(s, "Device control:\n"); | ||
254 | seq_printf(s, "Config Change %d ", dc->config_change); | ||
255 | seq_printf(s, "Vdev reset %d\n", dc->vdev_reset); | ||
256 | seq_printf(s, "Guest Ack %d ", dc->guest_ack); | ||
257 | seq_printf(s, "Host ack %d\n", dc->host_ack); | ||
258 | seq_printf(s, "Used address updated %d ", | ||
259 | dc->used_address_updated); | ||
260 | seq_printf(s, "Vdev 0x%llx\n", dc->vdev); | ||
261 | seq_printf(s, "c2h doorbell %d ", dc->c2h_vdev_db); | ||
262 | seq_printf(s, "h2c doorbell %d\n", dc->h2c_vdev_db); | ||
263 | } | ||
264 | |||
265 | return 0; | ||
266 | } | ||
267 | |||
268 | static int mic_dp_debug_open(struct inode *inode, struct file *file) | ||
269 | { | ||
270 | return single_open(file, mic_dp_show, inode->i_private); | ||
271 | } | ||
272 | |||
273 | static int mic_dp_debug_release(struct inode *inode, struct file *file) | ||
274 | { | ||
275 | return single_release(inode, file); | ||
276 | } | ||
277 | |||
278 | static const struct file_operations dp_ops = { | ||
279 | .owner = THIS_MODULE, | ||
280 | .open = mic_dp_debug_open, | ||
281 | .read = seq_read, | ||
282 | .llseek = seq_lseek, | ||
283 | .release = mic_dp_debug_release | ||
284 | }; | ||
285 | |||
286 | static int mic_vdev_info_show(struct seq_file *s, void *unused) | ||
287 | { | ||
288 | struct mic_device *mdev = s->private; | ||
289 | struct list_head *pos, *tmp; | ||
290 | struct mic_vdev *mvdev; | ||
291 | int i, j; | ||
292 | |||
293 | mutex_lock(&mdev->mic_mutex); | ||
294 | list_for_each_safe(pos, tmp, &mdev->vdev_list) { | ||
295 | mvdev = list_entry(pos, struct mic_vdev, list); | ||
296 | seq_printf(s, "VDEV type %d state %s in %ld out %ld\n", | ||
297 | mvdev->virtio_id, | ||
298 | mic_vdevup(mvdev) ? "UP" : "DOWN", | ||
299 | mvdev->in_bytes, | ||
300 | mvdev->out_bytes); | ||
301 | for (i = 0; i < MIC_MAX_VRINGS; i++) { | ||
302 | struct vring_desc *desc; | ||
303 | struct vring_avail *avail; | ||
304 | struct vring_used *used; | ||
305 | struct mic_vringh *mvr = &mvdev->mvr[i]; | ||
306 | struct vringh *vrh = &mvr->vrh; | ||
307 | int num = vrh->vring.num; | ||
308 | if (!num) | ||
309 | continue; | ||
310 | desc = vrh->vring.desc; | ||
311 | seq_printf(s, "vring i %d avail_idx %d", | ||
312 | i, mvr->vring.info->avail_idx & (num - 1)); | ||
313 | seq_printf(s, " vring i %d avail_idx %d\n", | ||
314 | i, mvr->vring.info->avail_idx); | ||
315 | seq_printf(s, "vrh i %d weak_barriers %d", | ||
316 | i, vrh->weak_barriers); | ||
317 | seq_printf(s, " last_avail_idx %d last_used_idx %d", | ||
318 | vrh->last_avail_idx, vrh->last_used_idx); | ||
319 | seq_printf(s, " completed %d\n", vrh->completed); | ||
320 | for (j = 0; j < num; j++) { | ||
321 | seq_printf(s, "desc[%d] addr 0x%llx len %d", | ||
322 | j, desc->addr, desc->len); | ||
323 | seq_printf(s, " flags 0x%x next %d\n", | ||
324 | desc->flags, desc->next); | ||
325 | desc++; | ||
326 | } | ||
327 | avail = vrh->vring.avail; | ||
328 | seq_printf(s, "avail flags 0x%x idx %d\n", | ||
329 | avail->flags, avail->idx & (num - 1)); | ||
330 | seq_printf(s, "avail flags 0x%x idx %d\n", | ||
331 | avail->flags, avail->idx); | ||
332 | for (j = 0; j < num; j++) | ||
333 | seq_printf(s, "avail ring[%d] %d\n", | ||
334 | j, avail->ring[j]); | ||
335 | used = vrh->vring.used; | ||
336 | seq_printf(s, "used flags 0x%x idx %d\n", | ||
337 | used->flags, used->idx & (num - 1)); | ||
338 | seq_printf(s, "used flags 0x%x idx %d\n", | ||
339 | used->flags, used->idx); | ||
340 | for (j = 0; j < num; j++) | ||
341 | seq_printf(s, "used ring[%d] id %d len %d\n", | ||
342 | j, used->ring[j].id, | ||
343 | used->ring[j].len); | ||
344 | } | ||
345 | } | ||
346 | mutex_unlock(&mdev->mic_mutex); | ||
347 | |||
348 | return 0; | ||
349 | } | ||
350 | |||
351 | static int mic_vdev_info_debug_open(struct inode *inode, struct file *file) | ||
352 | { | ||
353 | return single_open(file, mic_vdev_info_show, inode->i_private); | ||
354 | } | ||
355 | |||
356 | static int mic_vdev_info_debug_release(struct inode *inode, struct file *file) | ||
357 | { | ||
358 | return single_release(inode, file); | ||
359 | } | ||
360 | |||
361 | static const struct file_operations vdev_info_ops = { | ||
362 | .owner = THIS_MODULE, | ||
363 | .open = mic_vdev_info_debug_open, | ||
364 | .read = seq_read, | ||
365 | .llseek = seq_lseek, | ||
366 | .release = mic_vdev_info_debug_release | ||
367 | }; | ||
368 | |||
369 | static int mic_msi_irq_info_show(struct seq_file *s, void *pos) | ||
370 | { | ||
371 | struct mic_device *mdev = s->private; | ||
372 | int reg; | ||
373 | int i, j; | ||
374 | u16 entry; | ||
375 | u16 vector; | ||
376 | struct pci_dev *pdev = container_of(mdev->sdev->parent, | ||
377 | struct pci_dev, dev); | ||
378 | |||
379 | if (pci_dev_msi_enabled(pdev)) { | ||
380 | for (i = 0; i < mdev->irq_info.num_vectors; i++) { | ||
381 | if (pdev->msix_enabled) { | ||
382 | entry = mdev->irq_info.msix_entries[i].entry; | ||
383 | vector = mdev->irq_info.msix_entries[i].vector; | ||
384 | } else { | ||
385 | entry = 0; | ||
386 | vector = pdev->irq; | ||
387 | } | ||
388 | |||
389 | reg = mdev->intr_ops->read_msi_to_src_map(mdev, entry); | ||
390 | |||
391 | seq_printf(s, "%s %-10d %s %-10d MXAR[%d]: %08X\n", | ||
392 | "IRQ:", vector, "Entry:", entry, i, reg); | ||
393 | |||
394 | seq_printf(s, "%-10s", "offset:"); | ||
395 | for (j = (MIC_NUM_OFFSETS - 1); j >= 0; j--) | ||
396 | seq_printf(s, "%4d ", j); | ||
397 | seq_puts(s, "\n"); | ||
398 | |||
399 | |||
400 | seq_printf(s, "%-10s", "count:"); | ||
401 | for (j = (MIC_NUM_OFFSETS - 1); j >= 0; j--) | ||
402 | seq_printf(s, "%4d ", | ||
403 | (mdev->irq_info.mic_msi_map[i] & | ||
404 | BIT(j)) ? 1 : 0); | ||
405 | seq_puts(s, "\n\n"); | ||
406 | } | ||
407 | } else { | ||
408 | seq_puts(s, "MSI/MSIx interrupts not enabled\n"); | ||
409 | } | ||
410 | |||
411 | return 0; | ||
412 | } | ||
413 | |||
414 | static int mic_msi_irq_info_debug_open(struct inode *inode, struct file *file) | ||
415 | { | ||
416 | return single_open(file, mic_msi_irq_info_show, inode->i_private); | ||
417 | } | ||
418 | |||
419 | static int | ||
420 | mic_msi_irq_info_debug_release(struct inode *inode, struct file *file) | ||
421 | { | ||
422 | return single_release(inode, file); | ||
423 | } | ||
424 | |||
425 | static const struct file_operations msi_irq_info_ops = { | ||
426 | .owner = THIS_MODULE, | ||
427 | .open = mic_msi_irq_info_debug_open, | ||
428 | .read = seq_read, | ||
429 | .llseek = seq_lseek, | ||
430 | .release = mic_msi_irq_info_debug_release | ||
431 | }; | ||
432 | |||
433 | /** | ||
434 | * mic_create_debug_dir - Initialize MIC debugfs entries. | ||
435 | */ | ||
436 | void mic_create_debug_dir(struct mic_device *mdev) | ||
437 | { | ||
438 | if (!mic_dbg) | ||
439 | return; | ||
440 | |||
441 | mdev->dbg_dir = debugfs_create_dir(dev_name(mdev->sdev), mic_dbg); | ||
442 | if (!mdev->dbg_dir) | ||
443 | return; | ||
444 | |||
445 | debugfs_create_file("log_buf", 0444, mdev->dbg_dir, mdev, &log_buf_ops); | ||
446 | |||
447 | debugfs_create_file("smpt", 0444, mdev->dbg_dir, mdev, &smpt_file_ops); | ||
448 | |||
449 | debugfs_create_file("soft_reset", 0444, mdev->dbg_dir, mdev, | ||
450 | &soft_reset_ops); | ||
451 | |||
452 | debugfs_create_file("post_code", 0444, mdev->dbg_dir, mdev, | ||
453 | &post_code_ops); | ||
454 | |||
455 | debugfs_create_file("dp", 0444, mdev->dbg_dir, mdev, &dp_ops); | ||
456 | |||
457 | debugfs_create_file("vdev_info", 0444, mdev->dbg_dir, mdev, | ||
458 | &vdev_info_ops); | ||
459 | |||
460 | debugfs_create_file("msi_irq_info", 0444, mdev->dbg_dir, mdev, | ||
461 | &msi_irq_info_ops); | ||
462 | } | ||
463 | |||
464 | /** | ||
465 | * mic_delete_debug_dir - Uninitialize MIC debugfs entries. | ||
466 | */ | ||
467 | void mic_delete_debug_dir(struct mic_device *mdev) | ||
468 | { | ||
469 | if (!mdev->dbg_dir) | ||
470 | return; | ||
471 | |||
472 | debugfs_remove_recursive(mdev->dbg_dir); | ||
473 | } | ||
474 | |||
475 | /** | ||
476 | * mic_init_debugfs - Initialize global debugfs entry. | ||
477 | */ | ||
478 | void __init mic_init_debugfs(void) | ||
479 | { | ||
480 | mic_dbg = debugfs_create_dir(KBUILD_MODNAME, NULL); | ||
481 | if (!mic_dbg) | ||
482 | pr_err("can't create debugfs dir\n"); | ||
483 | } | ||
484 | |||
485 | /** | ||
486 | * mic_exit_debugfs - Uninitialize global debugfs entry | ||
487 | */ | ||
488 | void mic_exit_debugfs(void) | ||
489 | { | ||
490 | debugfs_remove(mic_dbg); | ||
491 | } | ||
diff --git a/drivers/misc/mic/host/mic_device.h b/drivers/misc/mic/host/mic_device.h new file mode 100644 index 000000000000..3574cc375bb9 --- /dev/null +++ b/drivers/misc/mic/host/mic_device.h | |||
@@ -0,0 +1,203 @@ | |||
1 | /* | ||
2 | * Intel MIC Platform Software Stack (MPSS) | ||
3 | * | ||
4 | * Copyright(c) 2013 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 Host driver. | ||
19 | * | ||
20 | */ | ||
21 | #ifndef _MIC_DEVICE_H_ | ||
22 | #define _MIC_DEVICE_H_ | ||
23 | |||
24 | #include <linux/cdev.h> | ||
25 | #include <linux/idr.h> | ||
26 | #include <linux/notifier.h> | ||
27 | |||
28 | #include "mic_intr.h" | ||
29 | |||
30 | /* The maximum number of MIC devices supported in a single host system. */ | ||
31 | #define MIC_MAX_NUM_DEVS 256 | ||
32 | |||
33 | /** | ||
34 | * enum mic_hw_family - The hardware family to which a device belongs. | ||
35 | */ | ||
36 | enum mic_hw_family { | ||
37 | MIC_FAMILY_X100 = 0, | ||
38 | MIC_FAMILY_UNKNOWN | ||
39 | }; | ||
40 | |||
41 | /** | ||
42 | * enum mic_stepping - MIC stepping ids. | ||
43 | */ | ||
44 | enum mic_stepping { | ||
45 | MIC_A0_STEP = 0x0, | ||
46 | MIC_B0_STEP = 0x10, | ||
47 | MIC_B1_STEP = 0x11, | ||
48 | MIC_C0_STEP = 0x20, | ||
49 | }; | ||
50 | |||
51 | /** | ||
52 | * struct mic_device - MIC device information for each card. | ||
53 | * | ||
54 | * @mmio: MMIO bar information. | ||
55 | * @aper: Aperture bar information. | ||
56 | * @family: The MIC family to which this device belongs. | ||
57 | * @ops: MIC HW specific operations. | ||
58 | * @id: The unique device id for this MIC device. | ||
59 | * @stepping: Stepping ID. | ||
60 | * @attr_group: Pointer to list of sysfs attribute groups. | ||
61 | * @sdev: Device for sysfs entries. | ||
62 | * @mic_mutex: Mutex for synchronizing access to mic_device. | ||
63 | * @intr_ops: HW specific interrupt operations. | ||
64 | * @smpt_ops: Hardware specific SMPT operations. | ||
65 | * @smpt: MIC SMPT information. | ||
66 | * @intr_info: H/W specific interrupt information. | ||
67 | * @irq_info: The OS specific irq information | ||
68 | * @dbg_dir: debugfs directory of this MIC device. | ||
69 | * @cmdline: Kernel command line. | ||
70 | * @firmware: Firmware file name. | ||
71 | * @ramdisk: Ramdisk file name. | ||
72 | * @bootmode: Boot mode i.e. "linux" or "elf" for flash updates. | ||
73 | * @bootaddr: MIC boot address. | ||
74 | * @reset_trigger_work: Work for triggering reset requests. | ||
75 | * @shutdown_work: Work for handling shutdown interrupts. | ||
76 | * @state: MIC state. | ||
77 | * @shutdown_status: MIC status reported by card for shutdown/crashes. | ||
78 | * @state_sysfs: Sysfs dirent for notifying ring 3 about MIC state changes. | ||
79 | * @reset_wait: Waitqueue for sleeping while reset completes. | ||
80 | * @log_buf_addr: Log buffer address for MIC. | ||
81 | * @log_buf_len: Log buffer length address for MIC. | ||
82 | * @dp: virtio device page | ||
83 | * @dp_dma_addr: virtio device page DMA address. | ||
84 | * @shutdown_db: shutdown doorbell. | ||
85 | * @shutdown_cookie: shutdown cookie. | ||
86 | * @cdev: Character device for MIC. | ||
87 | * @vdev_list: list of virtio devices. | ||
88 | * @pm_notifier: Handles PM notifications from the OS. | ||
89 | */ | ||
90 | struct mic_device { | ||
91 | struct mic_mw mmio; | ||
92 | struct mic_mw aper; | ||
93 | enum mic_hw_family family; | ||
94 | struct mic_hw_ops *ops; | ||
95 | int id; | ||
96 | enum mic_stepping stepping; | ||
97 | const struct attribute_group **attr_group; | ||
98 | struct device *sdev; | ||
99 | struct mutex mic_mutex; | ||
100 | struct mic_hw_intr_ops *intr_ops; | ||
101 | struct mic_smpt_ops *smpt_ops; | ||
102 | struct mic_smpt_info *smpt; | ||
103 | struct mic_intr_info *intr_info; | ||
104 | struct mic_irq_info irq_info; | ||
105 | struct dentry *dbg_dir; | ||
106 | char *cmdline; | ||
107 | char *firmware; | ||
108 | char *ramdisk; | ||
109 | char *bootmode; | ||
110 | u32 bootaddr; | ||
111 | struct work_struct reset_trigger_work; | ||
112 | struct work_struct shutdown_work; | ||
113 | u8 state; | ||
114 | u8 shutdown_status; | ||
115 | struct sysfs_dirent *state_sysfs; | ||
116 | struct completion reset_wait; | ||
117 | void *log_buf_addr; | ||
118 | int *log_buf_len; | ||
119 | void *dp; | ||
120 | dma_addr_t dp_dma_addr; | ||
121 | int shutdown_db; | ||
122 | struct mic_irq *shutdown_cookie; | ||
123 | struct cdev cdev; | ||
124 | struct list_head vdev_list; | ||
125 | struct notifier_block pm_notifier; | ||
126 | }; | ||
127 | |||
128 | /** | ||
129 | * struct mic_hw_ops - MIC HW specific operations. | ||
130 | * @aper_bar: Aperture bar resource number. | ||
131 | * @mmio_bar: MMIO bar resource number. | ||
132 | * @read_spad: Read from scratch pad register. | ||
133 | * @write_spad: Write to scratch pad register. | ||
134 | * @send_intr: Send an interrupt for a particular doorbell on the card. | ||
135 | * @ack_interrupt: Hardware specific operations to ack the h/w on | ||
136 | * receipt of an interrupt. | ||
137 | * @reset: Reset the remote processor. | ||
138 | * @reset_fw_ready: Reset firmware ready field. | ||
139 | * @is_fw_ready: Check if firmware is ready for OS download. | ||
140 | * @send_firmware_intr: Send an interrupt to the card firmware. | ||
141 | * @load_mic_fw: Load firmware segments required to boot the card | ||
142 | * into card memory. This includes the kernel, command line, ramdisk etc. | ||
143 | * @get_postcode: Get post code status from firmware. | ||
144 | */ | ||
145 | struct mic_hw_ops { | ||
146 | u8 aper_bar; | ||
147 | u8 mmio_bar; | ||
148 | u32 (*read_spad)(struct mic_device *mdev, unsigned int idx); | ||
149 | void (*write_spad)(struct mic_device *mdev, unsigned int idx, u32 val); | ||
150 | void (*send_intr)(struct mic_device *mdev, int doorbell); | ||
151 | u32 (*ack_interrupt)(struct mic_device *mdev); | ||
152 | void (*reset)(struct mic_device *mdev); | ||
153 | void (*reset_fw_ready)(struct mic_device *mdev); | ||
154 | bool (*is_fw_ready)(struct mic_device *mdev); | ||
155 | void (*send_firmware_intr)(struct mic_device *mdev); | ||
156 | int (*load_mic_fw)(struct mic_device *mdev, const char *buf); | ||
157 | u32 (*get_postcode)(struct mic_device *mdev); | ||
158 | }; | ||
159 | |||
160 | /** | ||
161 | * mic_mmio_read - read from an MMIO register. | ||
162 | * @mw: MMIO register base virtual address. | ||
163 | * @offset: register offset. | ||
164 | * | ||
165 | * RETURNS: register value. | ||
166 | */ | ||
167 | static inline u32 mic_mmio_read(struct mic_mw *mw, u32 offset) | ||
168 | { | ||
169 | return ioread32(mw->va + offset); | ||
170 | } | ||
171 | |||
172 | /** | ||
173 | * mic_mmio_write - write to an MMIO register. | ||
174 | * @mw: MMIO register base virtual address. | ||
175 | * @val: the data value to put into the register | ||
176 | * @offset: register offset. | ||
177 | * | ||
178 | * RETURNS: none. | ||
179 | */ | ||
180 | static inline void | ||
181 | mic_mmio_write(struct mic_mw *mw, u32 val, u32 offset) | ||
182 | { | ||
183 | iowrite32(val, mw->va + offset); | ||
184 | } | ||
185 | |||
186 | void mic_sysfs_init(struct mic_device *mdev); | ||
187 | int mic_start(struct mic_device *mdev, const char *buf); | ||
188 | void mic_stop(struct mic_device *mdev, bool force); | ||
189 | void mic_shutdown(struct mic_device *mdev); | ||
190 | void mic_reset_delayed_work(struct work_struct *work); | ||
191 | void mic_reset_trigger_work(struct work_struct *work); | ||
192 | void mic_shutdown_work(struct work_struct *work); | ||
193 | void mic_bootparam_init(struct mic_device *mdev); | ||
194 | void mic_set_state(struct mic_device *mdev, u8 state); | ||
195 | void mic_set_shutdown_status(struct mic_device *mdev, u8 status); | ||
196 | void mic_create_debug_dir(struct mic_device *dev); | ||
197 | void mic_delete_debug_dir(struct mic_device *dev); | ||
198 | void __init mic_init_debugfs(void); | ||
199 | void mic_exit_debugfs(void); | ||
200 | void mic_prepare_suspend(struct mic_device *mdev); | ||
201 | void mic_complete_resume(struct mic_device *mdev); | ||
202 | void mic_suspend(struct mic_device *mdev); | ||
203 | #endif | ||
diff --git a/drivers/misc/mic/host/mic_fops.c b/drivers/misc/mic/host/mic_fops.c new file mode 100644 index 000000000000..85776d7327f3 --- /dev/null +++ b/drivers/misc/mic/host/mic_fops.c | |||
@@ -0,0 +1,222 @@ | |||
1 | /* | ||
2 | * Intel MIC Platform Software Stack (MPSS) | ||
3 | * | ||
4 | * Copyright(c) 2013 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 Host driver. | ||
19 | * | ||
20 | */ | ||
21 | #include <linux/poll.h> | ||
22 | #include <linux/pci.h> | ||
23 | |||
24 | #include <linux/mic_common.h> | ||
25 | #include "../common/mic_dev.h" | ||
26 | #include "mic_device.h" | ||
27 | #include "mic_fops.h" | ||
28 | #include "mic_virtio.h" | ||
29 | |||
30 | int mic_open(struct inode *inode, struct file *f) | ||
31 | { | ||
32 | struct mic_vdev *mvdev; | ||
33 | struct mic_device *mdev = container_of(inode->i_cdev, | ||
34 | struct mic_device, cdev); | ||
35 | |||
36 | mvdev = kzalloc(sizeof(*mvdev), GFP_KERNEL); | ||
37 | if (!mvdev) | ||
38 | return -ENOMEM; | ||
39 | |||
40 | init_waitqueue_head(&mvdev->waitq); | ||
41 | INIT_LIST_HEAD(&mvdev->list); | ||
42 | mvdev->mdev = mdev; | ||
43 | mvdev->virtio_id = -1; | ||
44 | |||
45 | f->private_data = mvdev; | ||
46 | return 0; | ||
47 | } | ||
48 | |||
49 | int mic_release(struct inode *inode, struct file *f) | ||
50 | { | ||
51 | struct mic_vdev *mvdev = (struct mic_vdev *)f->private_data; | ||
52 | |||
53 | if (-1 != mvdev->virtio_id) | ||
54 | mic_virtio_del_device(mvdev); | ||
55 | f->private_data = NULL; | ||
56 | kfree(mvdev); | ||
57 | return 0; | ||
58 | } | ||
59 | |||
60 | long mic_ioctl(struct file *f, unsigned int cmd, unsigned long arg) | ||
61 | { | ||
62 | struct mic_vdev *mvdev = (struct mic_vdev *)f->private_data; | ||
63 | void __user *argp = (void __user *)arg; | ||
64 | int ret; | ||
65 | |||
66 | switch (cmd) { | ||
67 | case MIC_VIRTIO_ADD_DEVICE: | ||
68 | { | ||
69 | ret = mic_virtio_add_device(mvdev, argp); | ||
70 | if (ret < 0) { | ||
71 | dev_err(mic_dev(mvdev), | ||
72 | "%s %d errno ret %d\n", | ||
73 | __func__, __LINE__, ret); | ||
74 | return ret; | ||
75 | } | ||
76 | break; | ||
77 | } | ||
78 | case MIC_VIRTIO_COPY_DESC: | ||
79 | { | ||
80 | struct mic_copy_desc copy; | ||
81 | |||
82 | ret = mic_vdev_inited(mvdev); | ||
83 | if (ret) | ||
84 | return ret; | ||
85 | |||
86 | if (copy_from_user(©, argp, sizeof(copy))) | ||
87 | return -EFAULT; | ||
88 | |||
89 | dev_dbg(mic_dev(mvdev), | ||
90 | "%s %d === iovcnt 0x%x vr_idx 0x%x update_used %d\n", | ||
91 | __func__, __LINE__, copy.iovcnt, copy.vr_idx, | ||
92 | copy.update_used); | ||
93 | |||
94 | ret = mic_virtio_copy_desc(mvdev, ©); | ||
95 | if (ret < 0) { | ||
96 | dev_err(mic_dev(mvdev), | ||
97 | "%s %d errno ret %d\n", | ||
98 | __func__, __LINE__, ret); | ||
99 | return ret; | ||
100 | } | ||
101 | if (copy_to_user( | ||
102 | &((struct mic_copy_desc __user *)argp)->out_len, | ||
103 | ©.out_len, sizeof(copy.out_len))) { | ||
104 | dev_err(mic_dev(mvdev), "%s %d errno ret %d\n", | ||
105 | __func__, __LINE__, -EFAULT); | ||
106 | return -EFAULT; | ||
107 | } | ||
108 | break; | ||
109 | } | ||
110 | case MIC_VIRTIO_CONFIG_CHANGE: | ||
111 | { | ||
112 | ret = mic_vdev_inited(mvdev); | ||
113 | if (ret) | ||
114 | return ret; | ||
115 | |||
116 | ret = mic_virtio_config_change(mvdev, argp); | ||
117 | if (ret < 0) { | ||
118 | dev_err(mic_dev(mvdev), | ||
119 | "%s %d errno ret %d\n", | ||
120 | __func__, __LINE__, ret); | ||
121 | return ret; | ||
122 | } | ||
123 | break; | ||
124 | } | ||
125 | default: | ||
126 | return -ENOIOCTLCMD; | ||
127 | }; | ||
128 | return 0; | ||
129 | } | ||
130 | |||
131 | /* | ||
132 | * We return POLLIN | POLLOUT from poll when new buffers are enqueued, and | ||
133 | * not when previously enqueued buffers may be available. This means that | ||
134 | * in the card->host (TX) path, when userspace is unblocked by poll it | ||
135 | * must drain all available descriptors or it can stall. | ||
136 | */ | ||
137 | unsigned int mic_poll(struct file *f, poll_table *wait) | ||
138 | { | ||
139 | struct mic_vdev *mvdev = (struct mic_vdev *)f->private_data; | ||
140 | int mask = 0; | ||
141 | |||
142 | poll_wait(f, &mvdev->waitq, wait); | ||
143 | |||
144 | if (mic_vdev_inited(mvdev)) { | ||
145 | mask = POLLERR; | ||
146 | } else if (mvdev->poll_wake) { | ||
147 | mvdev->poll_wake = 0; | ||
148 | mask = POLLIN | POLLOUT; | ||
149 | } | ||
150 | |||
151 | return mask; | ||
152 | } | ||
153 | |||
154 | static inline int | ||
155 | mic_query_offset(struct mic_vdev *mvdev, unsigned long offset, | ||
156 | unsigned long *size, unsigned long *pa) | ||
157 | { | ||
158 | struct mic_device *mdev = mvdev->mdev; | ||
159 | unsigned long start = MIC_DP_SIZE; | ||
160 | int i; | ||
161 | |||
162 | /* | ||
163 | * MMAP interface is as follows: | ||
164 | * offset region | ||
165 | * 0x0 virtio device_page | ||
166 | * 0x1000 first vring | ||
167 | * 0x1000 + size of 1st vring second vring | ||
168 | * .... | ||
169 | */ | ||
170 | if (!offset) { | ||
171 | *pa = virt_to_phys(mdev->dp); | ||
172 | *size = MIC_DP_SIZE; | ||
173 | return 0; | ||
174 | } | ||
175 | |||
176 | for (i = 0; i < mvdev->dd->num_vq; i++) { | ||
177 | struct mic_vringh *mvr = &mvdev->mvr[i]; | ||
178 | if (offset == start) { | ||
179 | *pa = virt_to_phys(mvr->vring.va); | ||
180 | *size = mvr->vring.len; | ||
181 | return 0; | ||
182 | } | ||
183 | start += mvr->vring.len; | ||
184 | } | ||
185 | return -1; | ||
186 | } | ||
187 | |||
188 | /* | ||
189 | * Maps the device page and virtio rings to user space for readonly access. | ||
190 | */ | ||
191 | int | ||
192 | mic_mmap(struct file *f, struct vm_area_struct *vma) | ||
193 | { | ||
194 | struct mic_vdev *mvdev = (struct mic_vdev *)f->private_data; | ||
195 | unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; | ||
196 | unsigned long pa, size = vma->vm_end - vma->vm_start, size_rem = size; | ||
197 | int i, err; | ||
198 | |||
199 | err = mic_vdev_inited(mvdev); | ||
200 | if (err) | ||
201 | return err; | ||
202 | |||
203 | if (vma->vm_flags & VM_WRITE) | ||
204 | return -EACCES; | ||
205 | |||
206 | while (size_rem) { | ||
207 | i = mic_query_offset(mvdev, offset, &size, &pa); | ||
208 | if (i < 0) | ||
209 | return -EINVAL; | ||
210 | err = remap_pfn_range(vma, vma->vm_start + offset, | ||
211 | pa >> PAGE_SHIFT, size, vma->vm_page_prot); | ||
212 | if (err) | ||
213 | return err; | ||
214 | dev_dbg(mic_dev(mvdev), | ||
215 | "%s %d type %d size 0x%lx off 0x%lx pa 0x%lx vma 0x%lx\n", | ||
216 | __func__, __LINE__, mvdev->virtio_id, size, offset, | ||
217 | pa, vma->vm_start + offset); | ||
218 | size_rem -= size; | ||
219 | offset += size; | ||
220 | } | ||
221 | return 0; | ||
222 | } | ||
diff --git a/drivers/misc/mic/host/mic_fops.h b/drivers/misc/mic/host/mic_fops.h new file mode 100644 index 000000000000..dc3893dff667 --- /dev/null +++ b/drivers/misc/mic/host/mic_fops.h | |||
@@ -0,0 +1,32 @@ | |||
1 | /* | ||
2 | * Intel MIC Platform Software Stack (MPSS) | ||
3 | * | ||
4 | * Copyright(c) 2013 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 Host driver. | ||
19 | * | ||
20 | */ | ||
21 | #ifndef _MIC_FOPS_H_ | ||
22 | #define _MIC_FOPS_H_ | ||
23 | |||
24 | int mic_open(struct inode *inode, struct file *filp); | ||
25 | int mic_release(struct inode *inode, struct file *filp); | ||
26 | ssize_t mic_read(struct file *filp, char __user *buf, | ||
27 | size_t count, loff_t *pos); | ||
28 | long mic_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); | ||
29 | int mic_mmap(struct file *f, struct vm_area_struct *vma); | ||
30 | unsigned int mic_poll(struct file *f, poll_table *wait); | ||
31 | |||
32 | #endif | ||
diff --git a/drivers/misc/mic/host/mic_intr.c b/drivers/misc/mic/host/mic_intr.c new file mode 100644 index 000000000000..f9c29bc918bc --- /dev/null +++ b/drivers/misc/mic/host/mic_intr.c | |||
@@ -0,0 +1,630 @@ | |||
1 | /* | ||
2 | * Intel MIC Platform Software Stack (MPSS) | ||
3 | * | ||
4 | * Copyright(c) 2013 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 Host driver. | ||
19 | * | ||
20 | */ | ||
21 | #include <linux/pci.h> | ||
22 | #include <linux/interrupt.h> | ||
23 | |||
24 | #include "../common/mic_dev.h" | ||
25 | #include "mic_device.h" | ||
26 | |||
27 | /* | ||
28 | * mic_invoke_callback - Invoke callback functions registered for | ||
29 | * the corresponding source id. | ||
30 | * | ||
31 | * @mdev: pointer to the mic_device instance | ||
32 | * @idx: The interrupt source id. | ||
33 | * | ||
34 | * Returns none. | ||
35 | */ | ||
36 | static inline void mic_invoke_callback(struct mic_device *mdev, int idx) | ||
37 | { | ||
38 | struct mic_intr_cb *intr_cb; | ||
39 | struct pci_dev *pdev = container_of(mdev->sdev->parent, | ||
40 | struct pci_dev, dev); | ||
41 | |||
42 | spin_lock(&mdev->irq_info.mic_intr_lock); | ||
43 | list_for_each_entry(intr_cb, &mdev->irq_info.cb_list[idx], list) | ||
44 | if (intr_cb->func) | ||
45 | intr_cb->func(pdev->irq, intr_cb->data); | ||
46 | spin_unlock(&mdev->irq_info.mic_intr_lock); | ||
47 | } | ||
48 | |||
49 | /** | ||
50 | * mic_interrupt - Generic interrupt handler for | ||
51 | * MSI and INTx based interrupts. | ||
52 | */ | ||
53 | static irqreturn_t mic_interrupt(int irq, void *dev) | ||
54 | { | ||
55 | struct mic_device *mdev = dev; | ||
56 | struct mic_intr_info *info = mdev->intr_info; | ||
57 | u32 mask; | ||
58 | int i; | ||
59 | |||
60 | mask = mdev->ops->ack_interrupt(mdev); | ||
61 | if (!mask) | ||
62 | return IRQ_NONE; | ||
63 | |||
64 | for (i = info->intr_start_idx[MIC_INTR_DB]; | ||
65 | i < info->intr_len[MIC_INTR_DB]; i++) | ||
66 | if (mask & BIT(i)) | ||
67 | mic_invoke_callback(mdev, i); | ||
68 | |||
69 | return IRQ_HANDLED; | ||
70 | } | ||
71 | |||
72 | /* Return the interrupt offset from the index. Index is 0 based. */ | ||
73 | static u16 mic_map_src_to_offset(struct mic_device *mdev, | ||
74 | int intr_src, enum mic_intr_type type) | ||
75 | { | ||
76 | if (type >= MIC_NUM_INTR_TYPES) | ||
77 | return MIC_NUM_OFFSETS; | ||
78 | if (intr_src >= mdev->intr_info->intr_len[type]) | ||
79 | return MIC_NUM_OFFSETS; | ||
80 | |||
81 | return mdev->intr_info->intr_start_idx[type] + intr_src; | ||
82 | } | ||
83 | |||
84 | /* Return next available msix_entry. */ | ||
85 | static struct msix_entry *mic_get_available_vector(struct mic_device *mdev) | ||
86 | { | ||
87 | int i; | ||
88 | struct mic_irq_info *info = &mdev->irq_info; | ||
89 | |||
90 | for (i = 0; i < info->num_vectors; i++) | ||
91 | if (!info->mic_msi_map[i]) | ||
92 | return &info->msix_entries[i]; | ||
93 | return NULL; | ||
94 | } | ||
95 | |||
96 | /** | ||
97 | * mic_register_intr_callback - Register a callback handler for the | ||
98 | * given source id. | ||
99 | * | ||
100 | * @mdev: pointer to the mic_device instance | ||
101 | * @idx: The source id to be registered. | ||
102 | * @func: The function to be called when the source id receives | ||
103 | * the interrupt. | ||
104 | * @data: Private data of the requester. | ||
105 | * Return the callback structure that was registered or an | ||
106 | * appropriate error on failure. | ||
107 | */ | ||
108 | static struct mic_intr_cb *mic_register_intr_callback(struct mic_device *mdev, | ||
109 | u8 idx, irqreturn_t (*func) (int irq, void *dev), | ||
110 | void *data) | ||
111 | { | ||
112 | struct mic_intr_cb *intr_cb; | ||
113 | unsigned long flags; | ||
114 | int rc; | ||
115 | intr_cb = kmalloc(sizeof(*intr_cb), GFP_KERNEL); | ||
116 | |||
117 | if (!intr_cb) | ||
118 | return ERR_PTR(-ENOMEM); | ||
119 | |||
120 | intr_cb->func = func; | ||
121 | intr_cb->data = data; | ||
122 | intr_cb->cb_id = ida_simple_get(&mdev->irq_info.cb_ida, | ||
123 | 0, 0, GFP_KERNEL); | ||
124 | if (intr_cb->cb_id < 0) { | ||
125 | rc = intr_cb->cb_id; | ||
126 | goto ida_fail; | ||
127 | } | ||
128 | |||
129 | spin_lock_irqsave(&mdev->irq_info.mic_intr_lock, flags); | ||
130 | list_add_tail(&intr_cb->list, &mdev->irq_info.cb_list[idx]); | ||
131 | spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags); | ||
132 | |||
133 | return intr_cb; | ||
134 | ida_fail: | ||
135 | kfree(intr_cb); | ||
136 | return ERR_PTR(rc); | ||
137 | } | ||
138 | |||
139 | /** | ||
140 | * mic_unregister_intr_callback - Unregister the callback handler | ||
141 | * identified by its callback id. | ||
142 | * | ||
143 | * @mdev: pointer to the mic_device instance | ||
144 | * @idx: The callback structure id to be unregistered. | ||
145 | * Return the source id that was unregistered or MIC_NUM_OFFSETS if no | ||
146 | * such callback handler was found. | ||
147 | */ | ||
148 | static u8 mic_unregister_intr_callback(struct mic_device *mdev, u32 idx) | ||
149 | { | ||
150 | struct list_head *pos, *tmp; | ||
151 | struct mic_intr_cb *intr_cb; | ||
152 | unsigned long flags; | ||
153 | int i; | ||
154 | |||
155 | for (i = 0; i < MIC_NUM_OFFSETS; i++) { | ||
156 | spin_lock_irqsave(&mdev->irq_info.mic_intr_lock, flags); | ||
157 | list_for_each_safe(pos, tmp, &mdev->irq_info.cb_list[i]) { | ||
158 | intr_cb = list_entry(pos, struct mic_intr_cb, list); | ||
159 | if (intr_cb->cb_id == idx) { | ||
160 | list_del(pos); | ||
161 | ida_simple_remove(&mdev->irq_info.cb_ida, | ||
162 | intr_cb->cb_id); | ||
163 | kfree(intr_cb); | ||
164 | spin_unlock_irqrestore( | ||
165 | &mdev->irq_info.mic_intr_lock, flags); | ||
166 | return i; | ||
167 | } | ||
168 | } | ||
169 | spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags); | ||
170 | } | ||
171 | return MIC_NUM_OFFSETS; | ||
172 | } | ||
173 | |||
174 | /** | ||
175 | * mic_setup_msix - Initializes MSIx interrupts. | ||
176 | * | ||
177 | * @mdev: pointer to mic_device instance | ||
178 | * | ||
179 | * | ||
180 | * RETURNS: An appropriate -ERRNO error value on error, or zero for success. | ||
181 | */ | ||
182 | static int mic_setup_msix(struct mic_device *mdev, struct pci_dev *pdev) | ||
183 | { | ||
184 | int rc, i; | ||
185 | int entry_size = sizeof(*mdev->irq_info.msix_entries); | ||
186 | |||
187 | mdev->irq_info.msix_entries = kmalloc_array(MIC_MIN_MSIX, | ||
188 | entry_size, GFP_KERNEL); | ||
189 | if (!mdev->irq_info.msix_entries) { | ||
190 | rc = -ENOMEM; | ||
191 | goto err_nomem1; | ||
192 | } | ||
193 | |||
194 | for (i = 0; i < MIC_MIN_MSIX; i++) | ||
195 | mdev->irq_info.msix_entries[i].entry = i; | ||
196 | |||
197 | rc = pci_enable_msix(pdev, mdev->irq_info.msix_entries, | ||
198 | MIC_MIN_MSIX); | ||
199 | if (rc) { | ||
200 | dev_dbg(&pdev->dev, "Error enabling MSIx. rc = %d\n", rc); | ||
201 | goto err_enable_msix; | ||
202 | } | ||
203 | |||
204 | mdev->irq_info.num_vectors = MIC_MIN_MSIX; | ||
205 | mdev->irq_info.mic_msi_map = kzalloc((sizeof(u32) * | ||
206 | mdev->irq_info.num_vectors), GFP_KERNEL); | ||
207 | |||
208 | if (!mdev->irq_info.mic_msi_map) { | ||
209 | rc = -ENOMEM; | ||
210 | goto err_nomem2; | ||
211 | } | ||
212 | |||
213 | dev_dbg(mdev->sdev->parent, | ||
214 | "%d MSIx irqs setup\n", mdev->irq_info.num_vectors); | ||
215 | return 0; | ||
216 | err_nomem2: | ||
217 | pci_disable_msix(pdev); | ||
218 | err_enable_msix: | ||
219 | kfree(mdev->irq_info.msix_entries); | ||
220 | err_nomem1: | ||
221 | mdev->irq_info.num_vectors = 0; | ||
222 | return rc; | ||
223 | } | ||
224 | |||
225 | /** | ||
226 | * mic_setup_callbacks - Initialize data structures needed | ||
227 | * to handle callbacks. | ||
228 | * | ||
229 | * @mdev: pointer to mic_device instance | ||
230 | */ | ||
231 | static int mic_setup_callbacks(struct mic_device *mdev) | ||
232 | { | ||
233 | int i; | ||
234 | |||
235 | mdev->irq_info.cb_list = kmalloc_array(MIC_NUM_OFFSETS, | ||
236 | sizeof(*mdev->irq_info.cb_list), | ||
237 | GFP_KERNEL); | ||
238 | if (!mdev->irq_info.cb_list) | ||
239 | return -ENOMEM; | ||
240 | |||
241 | for (i = 0; i < MIC_NUM_OFFSETS; i++) | ||
242 | INIT_LIST_HEAD(&mdev->irq_info.cb_list[i]); | ||
243 | ida_init(&mdev->irq_info.cb_ida); | ||
244 | spin_lock_init(&mdev->irq_info.mic_intr_lock); | ||
245 | return 0; | ||
246 | } | ||
247 | |||
248 | /** | ||
249 | * mic_release_callbacks - Uninitialize data structures needed | ||
250 | * to handle callbacks. | ||
251 | * | ||
252 | * @mdev: pointer to mic_device instance | ||
253 | */ | ||
254 | static void mic_release_callbacks(struct mic_device *mdev) | ||
255 | { | ||
256 | unsigned long flags; | ||
257 | struct list_head *pos, *tmp; | ||
258 | struct mic_intr_cb *intr_cb; | ||
259 | int i; | ||
260 | |||
261 | for (i = 0; i < MIC_NUM_OFFSETS; i++) { | ||
262 | spin_lock_irqsave(&mdev->irq_info.mic_intr_lock, flags); | ||
263 | |||
264 | if (list_empty(&mdev->irq_info.cb_list[i])) { | ||
265 | spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, | ||
266 | flags); | ||
267 | break; | ||
268 | } | ||
269 | |||
270 | list_for_each_safe(pos, tmp, &mdev->irq_info.cb_list[i]) { | ||
271 | intr_cb = list_entry(pos, struct mic_intr_cb, list); | ||
272 | list_del(pos); | ||
273 | ida_simple_remove(&mdev->irq_info.cb_ida, | ||
274 | intr_cb->cb_id); | ||
275 | kfree(intr_cb); | ||
276 | } | ||
277 | spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags); | ||
278 | } | ||
279 | ida_destroy(&mdev->irq_info.cb_ida); | ||
280 | kfree(mdev->irq_info.cb_list); | ||
281 | } | ||
282 | |||
283 | /** | ||
284 | * mic_setup_msi - Initializes MSI interrupts. | ||
285 | * | ||
286 | * @mdev: pointer to mic_device instance | ||
287 | * @pdev: PCI device structure | ||
288 | * | ||
289 | * RETURNS: An appropriate -ERRNO error value on error, or zero for success. | ||
290 | */ | ||
291 | static int mic_setup_msi(struct mic_device *mdev, struct pci_dev *pdev) | ||
292 | { | ||
293 | int rc; | ||
294 | |||
295 | rc = pci_enable_msi(pdev); | ||
296 | if (rc) { | ||
297 | dev_dbg(&pdev->dev, "Error enabling MSI. rc = %d\n", rc); | ||
298 | return rc; | ||
299 | } | ||
300 | |||
301 | mdev->irq_info.num_vectors = 1; | ||
302 | mdev->irq_info.mic_msi_map = kzalloc((sizeof(u32) * | ||
303 | mdev->irq_info.num_vectors), GFP_KERNEL); | ||
304 | |||
305 | if (!mdev->irq_info.mic_msi_map) { | ||
306 | rc = -ENOMEM; | ||
307 | goto err_nomem1; | ||
308 | } | ||
309 | |||
310 | rc = mic_setup_callbacks(mdev); | ||
311 | if (rc) { | ||
312 | dev_err(&pdev->dev, "Error setting up callbacks\n"); | ||
313 | goto err_nomem2; | ||
314 | } | ||
315 | |||
316 | rc = request_irq(pdev->irq, mic_interrupt, 0 , "mic-msi", mdev); | ||
317 | if (rc) { | ||
318 | dev_err(&pdev->dev, "Error allocating MSI interrupt\n"); | ||
319 | goto err_irq_req_fail; | ||
320 | } | ||
321 | |||
322 | dev_dbg(&pdev->dev, "%d MSI irqs setup\n", mdev->irq_info.num_vectors); | ||
323 | return 0; | ||
324 | err_irq_req_fail: | ||
325 | mic_release_callbacks(mdev); | ||
326 | err_nomem2: | ||
327 | kfree(mdev->irq_info.mic_msi_map); | ||
328 | err_nomem1: | ||
329 | pci_disable_msi(pdev); | ||
330 | mdev->irq_info.num_vectors = 0; | ||
331 | return rc; | ||
332 | } | ||
333 | |||
334 | /** | ||
335 | * mic_setup_intx - Initializes legacy interrupts. | ||
336 | * | ||
337 | * @mdev: pointer to mic_device instance | ||
338 | * @pdev: PCI device structure | ||
339 | * | ||
340 | * RETURNS: An appropriate -ERRNO error value on error, or zero for success. | ||
341 | */ | ||
342 | static int mic_setup_intx(struct mic_device *mdev, struct pci_dev *pdev) | ||
343 | { | ||
344 | int rc; | ||
345 | |||
346 | pci_msi_off(pdev); | ||
347 | |||
348 | /* Enable intx */ | ||
349 | pci_intx(pdev, 1); | ||
350 | rc = mic_setup_callbacks(mdev); | ||
351 | if (rc) { | ||
352 | dev_err(&pdev->dev, "Error setting up callbacks\n"); | ||
353 | goto err_nomem; | ||
354 | } | ||
355 | |||
356 | rc = request_irq(pdev->irq, mic_interrupt, | ||
357 | IRQF_SHARED, "mic-intx", mdev); | ||
358 | if (rc) | ||
359 | goto err; | ||
360 | |||
361 | dev_dbg(&pdev->dev, "intx irq setup\n"); | ||
362 | return 0; | ||
363 | err: | ||
364 | mic_release_callbacks(mdev); | ||
365 | err_nomem: | ||
366 | return rc; | ||
367 | } | ||
368 | |||
369 | /** | ||
370 | * mic_next_db - Retrieve the next doorbell interrupt source id. | ||
371 | * The id is picked sequentially from the available pool of | ||
372 | * doorlbell ids. | ||
373 | * | ||
374 | * @mdev: pointer to the mic_device instance. | ||
375 | * | ||
376 | * Returns the next doorbell interrupt source. | ||
377 | */ | ||
378 | int mic_next_db(struct mic_device *mdev) | ||
379 | { | ||
380 | int next_db; | ||
381 | |||
382 | next_db = mdev->irq_info.next_avail_src % | ||
383 | mdev->intr_info->intr_len[MIC_INTR_DB]; | ||
384 | mdev->irq_info.next_avail_src++; | ||
385 | return next_db; | ||
386 | } | ||
387 | |||
388 | #define COOKIE_ID_SHIFT 16 | ||
389 | #define GET_ENTRY(cookie) ((cookie) & 0xFFFF) | ||
390 | #define GET_OFFSET(cookie) ((cookie) >> COOKIE_ID_SHIFT) | ||
391 | #define MK_COOKIE(x, y) ((x) | (y) << COOKIE_ID_SHIFT) | ||
392 | |||
393 | /** | ||
394 | * mic_request_irq - request an irq. mic_mutex needs | ||
395 | * to be held before calling this function. | ||
396 | * | ||
397 | * @mdev: pointer to mic_device instance | ||
398 | * @func: The callback function that handles the interrupt. | ||
399 | * The function needs to call ack_interrupts | ||
400 | * (mdev->ops->ack_interrupt(mdev)) when handling the interrupts. | ||
401 | * @name: The ASCII name of the callee requesting the irq. | ||
402 | * @data: private data that is returned back when calling the | ||
403 | * function handler. | ||
404 | * @intr_src: The source id of the requester. Its the doorbell id | ||
405 | * for Doorbell interrupts and DMA channel id for DMA interrupts. | ||
406 | * @type: The type of interrupt. Values defined in mic_intr_type | ||
407 | * | ||
408 | * returns: The cookie that is transparent to the caller. Passed | ||
409 | * back when calling mic_free_irq. An appropriate error code | ||
410 | * is returned on failure. Caller needs to use IS_ERR(return_val) | ||
411 | * to check for failure and PTR_ERR(return_val) to obtained the | ||
412 | * error code. | ||
413 | * | ||
414 | */ | ||
415 | struct mic_irq *mic_request_irq(struct mic_device *mdev, | ||
416 | irqreturn_t (*func)(int irq, void *dev), | ||
417 | const char *name, void *data, int intr_src, | ||
418 | enum mic_intr_type type) | ||
419 | { | ||
420 | u16 offset; | ||
421 | int rc = 0; | ||
422 | struct msix_entry *msix = NULL; | ||
423 | unsigned long cookie = 0; | ||
424 | u16 entry; | ||
425 | struct mic_intr_cb *intr_cb; | ||
426 | struct pci_dev *pdev = container_of(mdev->sdev->parent, | ||
427 | struct pci_dev, dev); | ||
428 | |||
429 | offset = mic_map_src_to_offset(mdev, intr_src, type); | ||
430 | if (offset >= MIC_NUM_OFFSETS) { | ||
431 | dev_err(mdev->sdev->parent, | ||
432 | "Error mapping index %d to a valid source id.\n", | ||
433 | intr_src); | ||
434 | rc = -EINVAL; | ||
435 | goto err; | ||
436 | } | ||
437 | |||
438 | if (mdev->irq_info.num_vectors > 1) { | ||
439 | msix = mic_get_available_vector(mdev); | ||
440 | if (!msix) { | ||
441 | dev_err(mdev->sdev->parent, | ||
442 | "No MSIx vectors available for use.\n"); | ||
443 | rc = -ENOSPC; | ||
444 | goto err; | ||
445 | } | ||
446 | |||
447 | rc = request_irq(msix->vector, func, 0, name, data); | ||
448 | if (rc) { | ||
449 | dev_dbg(mdev->sdev->parent, | ||
450 | "request irq failed rc = %d\n", rc); | ||
451 | goto err; | ||
452 | } | ||
453 | entry = msix->entry; | ||
454 | mdev->irq_info.mic_msi_map[entry] |= BIT(offset); | ||
455 | mdev->intr_ops->program_msi_to_src_map(mdev, | ||
456 | entry, offset, true); | ||
457 | cookie = MK_COOKIE(entry, offset); | ||
458 | dev_dbg(mdev->sdev->parent, "irq: %d assigned for src: %d\n", | ||
459 | msix->vector, intr_src); | ||
460 | } else { | ||
461 | intr_cb = mic_register_intr_callback(mdev, | ||
462 | offset, func, data); | ||
463 | if (IS_ERR(intr_cb)) { | ||
464 | dev_err(mdev->sdev->parent, | ||
465 | "No available callback entries for use\n"); | ||
466 | rc = PTR_ERR(intr_cb); | ||
467 | goto err; | ||
468 | } | ||
469 | |||
470 | entry = 0; | ||
471 | if (pci_dev_msi_enabled(pdev)) { | ||
472 | mdev->irq_info.mic_msi_map[entry] |= (1 << offset); | ||
473 | mdev->intr_ops->program_msi_to_src_map(mdev, | ||
474 | entry, offset, true); | ||
475 | } | ||
476 | cookie = MK_COOKIE(entry, intr_cb->cb_id); | ||
477 | dev_dbg(mdev->sdev->parent, "callback %d registered for src: %d\n", | ||
478 | intr_cb->cb_id, intr_src); | ||
479 | } | ||
480 | return (struct mic_irq *)cookie; | ||
481 | err: | ||
482 | return ERR_PTR(rc); | ||
483 | } | ||
484 | |||
485 | /** | ||
486 | * mic_free_irq - free irq. mic_mutex | ||
487 | * needs to be held before calling this function. | ||
488 | * | ||
489 | * @mdev: pointer to mic_device instance | ||
490 | * @cookie: cookie obtained during a successful call to mic_request_irq | ||
491 | * @data: private data specified by the calling function during the | ||
492 | * mic_request_irq | ||
493 | * | ||
494 | * returns: none. | ||
495 | */ | ||
496 | void mic_free_irq(struct mic_device *mdev, | ||
497 | struct mic_irq *cookie, void *data) | ||
498 | { | ||
499 | u32 offset; | ||
500 | u32 entry; | ||
501 | u8 src_id; | ||
502 | unsigned int irq; | ||
503 | struct pci_dev *pdev = container_of(mdev->sdev->parent, | ||
504 | struct pci_dev, dev); | ||
505 | |||
506 | entry = GET_ENTRY((unsigned long)cookie); | ||
507 | offset = GET_OFFSET((unsigned long)cookie); | ||
508 | if (mdev->irq_info.num_vectors > 1) { | ||
509 | if (entry >= mdev->irq_info.num_vectors) { | ||
510 | dev_warn(mdev->sdev->parent, | ||
511 | "entry %d should be < num_irq %d\n", | ||
512 | entry, mdev->irq_info.num_vectors); | ||
513 | return; | ||
514 | } | ||
515 | irq = mdev->irq_info.msix_entries[entry].vector; | ||
516 | free_irq(irq, data); | ||
517 | mdev->irq_info.mic_msi_map[entry] &= ~(BIT(offset)); | ||
518 | mdev->intr_ops->program_msi_to_src_map(mdev, | ||
519 | entry, offset, false); | ||
520 | |||
521 | dev_dbg(mdev->sdev->parent, "irq: %d freed\n", irq); | ||
522 | } else { | ||
523 | irq = pdev->irq; | ||
524 | src_id = mic_unregister_intr_callback(mdev, offset); | ||
525 | if (src_id >= MIC_NUM_OFFSETS) { | ||
526 | dev_warn(mdev->sdev->parent, "Error unregistering callback\n"); | ||
527 | return; | ||
528 | } | ||
529 | if (pci_dev_msi_enabled(pdev)) { | ||
530 | mdev->irq_info.mic_msi_map[entry] &= ~(BIT(src_id)); | ||
531 | mdev->intr_ops->program_msi_to_src_map(mdev, | ||
532 | entry, src_id, false); | ||
533 | } | ||
534 | dev_dbg(mdev->sdev->parent, "callback %d unregistered for src: %d\n", | ||
535 | offset, src_id); | ||
536 | } | ||
537 | } | ||
538 | |||
539 | /** | ||
540 | * mic_setup_interrupts - Initializes interrupts. | ||
541 | * | ||
542 | * @mdev: pointer to mic_device instance | ||
543 | * @pdev: PCI device structure | ||
544 | * | ||
545 | * RETURNS: An appropriate -ERRNO error value on error, or zero for success. | ||
546 | */ | ||
547 | int mic_setup_interrupts(struct mic_device *mdev, struct pci_dev *pdev) | ||
548 | { | ||
549 | int rc; | ||
550 | |||
551 | rc = mic_setup_msix(mdev, pdev); | ||
552 | if (!rc) | ||
553 | goto done; | ||
554 | |||
555 | rc = mic_setup_msi(mdev, pdev); | ||
556 | if (!rc) | ||
557 | goto done; | ||
558 | |||
559 | rc = mic_setup_intx(mdev, pdev); | ||
560 | if (rc) { | ||
561 | dev_err(mdev->sdev->parent, "no usable interrupts\n"); | ||
562 | return rc; | ||
563 | } | ||
564 | done: | ||
565 | mdev->intr_ops->enable_interrupts(mdev); | ||
566 | return 0; | ||
567 | } | ||
568 | |||
569 | /** | ||
570 | * mic_free_interrupts - Frees interrupts setup by mic_setup_interrupts | ||
571 | * | ||
572 | * @mdev: pointer to mic_device instance | ||
573 | * @pdev: PCI device structure | ||
574 | * | ||
575 | * returns none. | ||
576 | */ | ||
577 | void mic_free_interrupts(struct mic_device *mdev, struct pci_dev *pdev) | ||
578 | { | ||
579 | int i; | ||
580 | |||
581 | mdev->intr_ops->disable_interrupts(mdev); | ||
582 | if (mdev->irq_info.num_vectors > 1) { | ||
583 | for (i = 0; i < mdev->irq_info.num_vectors; i++) { | ||
584 | if (mdev->irq_info.mic_msi_map[i]) | ||
585 | dev_warn(&pdev->dev, "irq %d may still be in use.\n", | ||
586 | mdev->irq_info.msix_entries[i].vector); | ||
587 | } | ||
588 | kfree(mdev->irq_info.mic_msi_map); | ||
589 | kfree(mdev->irq_info.msix_entries); | ||
590 | pci_disable_msix(pdev); | ||
591 | } else { | ||
592 | if (pci_dev_msi_enabled(pdev)) { | ||
593 | free_irq(pdev->irq, mdev); | ||
594 | kfree(mdev->irq_info.mic_msi_map); | ||
595 | pci_disable_msi(pdev); | ||
596 | } else { | ||
597 | free_irq(pdev->irq, mdev); | ||
598 | } | ||
599 | mic_release_callbacks(mdev); | ||
600 | } | ||
601 | } | ||
602 | |||
603 | /** | ||
604 | * mic_intr_restore - Restore MIC interrupt registers. | ||
605 | * | ||
606 | * @mdev: pointer to mic_device instance. | ||
607 | * | ||
608 | * Restore the interrupt registers to values previously | ||
609 | * stored in the SW data structures. mic_mutex needs to | ||
610 | * be held before calling this function. | ||
611 | * | ||
612 | * returns None. | ||
613 | */ | ||
614 | void mic_intr_restore(struct mic_device *mdev) | ||
615 | { | ||
616 | int entry, offset; | ||
617 | struct pci_dev *pdev = container_of(mdev->sdev->parent, | ||
618 | struct pci_dev, dev); | ||
619 | |||
620 | if (!pci_dev_msi_enabled(pdev)) | ||
621 | return; | ||
622 | |||
623 | for (entry = 0; entry < mdev->irq_info.num_vectors; entry++) { | ||
624 | for (offset = 0; offset < MIC_NUM_OFFSETS; offset++) { | ||
625 | if (mdev->irq_info.mic_msi_map[entry] & BIT(offset)) | ||
626 | mdev->intr_ops->program_msi_to_src_map(mdev, | ||
627 | entry, offset, true); | ||
628 | } | ||
629 | } | ||
630 | } | ||
diff --git a/drivers/misc/mic/host/mic_intr.h b/drivers/misc/mic/host/mic_intr.h new file mode 100644 index 000000000000..6091aa97e116 --- /dev/null +++ b/drivers/misc/mic/host/mic_intr.h | |||
@@ -0,0 +1,137 @@ | |||
1 | /* | ||
2 | * Intel MIC Platform Software Stack (MPSS) | ||
3 | * | ||
4 | * Copyright(c) 2013 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 Host driver. | ||
19 | * | ||
20 | */ | ||
21 | #ifndef _MIC_INTR_H_ | ||
22 | #define _MIC_INTR_H_ | ||
23 | |||
24 | /* | ||
25 | * The minimum number of msix vectors required for normal operation. | ||
26 | * 3 for virtio network, console and block devices. | ||
27 | * 1 for card shutdown notifications. | ||
28 | */ | ||
29 | #define MIC_MIN_MSIX 4 | ||
30 | #define MIC_NUM_OFFSETS 32 | ||
31 | |||
32 | /** | ||
33 | * mic_intr_source - The type of source that will generate | ||
34 | * the interrupt.The number of types needs to be in sync with | ||
35 | * MIC_NUM_INTR_TYPES | ||
36 | * | ||
37 | * MIC_INTR_DB: The source is a doorbell | ||
38 | * MIC_INTR_DMA: The source is a DMA channel | ||
39 | * MIC_INTR_ERR: The source is an error interrupt e.g. SBOX ERR | ||
40 | * MIC_NUM_INTR_TYPES: Total number of interrupt sources. | ||
41 | */ | ||
42 | enum mic_intr_type { | ||
43 | MIC_INTR_DB = 0, | ||
44 | MIC_INTR_DMA, | ||
45 | MIC_INTR_ERR, | ||
46 | MIC_NUM_INTR_TYPES | ||
47 | }; | ||
48 | |||
49 | /** | ||
50 | * struct mic_intr_info - Contains h/w specific interrupt sources | ||
51 | * information. | ||
52 | * | ||
53 | * @intr_start_idx: Contains the starting indexes of the | ||
54 | * interrupt types. | ||
55 | * @intr_len: Contains the length of the interrupt types. | ||
56 | */ | ||
57 | struct mic_intr_info { | ||
58 | u16 intr_start_idx[MIC_NUM_INTR_TYPES]; | ||
59 | u16 intr_len[MIC_NUM_INTR_TYPES]; | ||
60 | }; | ||
61 | |||
62 | /** | ||
63 | * struct mic_irq_info - OS specific irq information | ||
64 | * | ||
65 | * @next_avail_src: next available doorbell that can be assigned. | ||
66 | * @msix_entries: msix entries allocated while setting up MSI-x | ||
67 | * @mic_msi_map: The MSI/MSI-x mapping information. | ||
68 | * @num_vectors: The number of MSI/MSI-x vectors that have been allocated. | ||
69 | * @cb_ida: callback ID allocator to track the callbacks registered. | ||
70 | * @mic_intr_lock: spinlock to protect the interrupt callback list. | ||
71 | * @cb_list: Array of callback lists one for each source. | ||
72 | */ | ||
73 | struct mic_irq_info { | ||
74 | int next_avail_src; | ||
75 | struct msix_entry *msix_entries; | ||
76 | u32 *mic_msi_map; | ||
77 | u16 num_vectors; | ||
78 | struct ida cb_ida; | ||
79 | spinlock_t mic_intr_lock; | ||
80 | struct list_head *cb_list; | ||
81 | }; | ||
82 | |||
83 | /** | ||
84 | * struct mic_intr_cb - Interrupt callback structure. | ||
85 | * | ||
86 | * @func: The callback function | ||
87 | * @data: Private data of the requester. | ||
88 | * @cb_id: The callback id. Identifies this callback. | ||
89 | * @list: list head pointing to the next callback structure. | ||
90 | */ | ||
91 | struct mic_intr_cb { | ||
92 | irqreturn_t (*func) (int irq, void *data); | ||
93 | void *data; | ||
94 | int cb_id; | ||
95 | struct list_head list; | ||
96 | }; | ||
97 | |||
98 | /** | ||
99 | * struct mic_irq - opaque pointer used as cookie | ||
100 | */ | ||
101 | struct mic_irq; | ||
102 | |||
103 | /* Forward declaration */ | ||
104 | struct mic_device; | ||
105 | |||
106 | /** | ||
107 | * struct mic_hw_intr_ops: MIC HW specific interrupt operations | ||
108 | * @intr_init: Initialize H/W specific interrupt information. | ||
109 | * @enable_interrupts: Enable interrupts from the hardware. | ||
110 | * @disable_interrupts: Disable interrupts from the hardware. | ||
111 | * @program_msi_to_src_map: Update MSI mapping registers with | ||
112 | * irq information. | ||
113 | * @read_msi_to_src_map: Read MSI mapping registers containing | ||
114 | * irq information. | ||
115 | */ | ||
116 | struct mic_hw_intr_ops { | ||
117 | void (*intr_init)(struct mic_device *mdev); | ||
118 | void (*enable_interrupts)(struct mic_device *mdev); | ||
119 | void (*disable_interrupts)(struct mic_device *mdev); | ||
120 | void (*program_msi_to_src_map) (struct mic_device *mdev, | ||
121 | int idx, int intr_src, bool set); | ||
122 | u32 (*read_msi_to_src_map) (struct mic_device *mdev, | ||
123 | int idx); | ||
124 | }; | ||
125 | |||
126 | int mic_next_db(struct mic_device *mdev); | ||
127 | struct mic_irq *mic_request_irq(struct mic_device *mdev, | ||
128 | irqreturn_t (*func)(int irq, void *data), | ||
129 | const char *name, void *data, int intr_src, | ||
130 | enum mic_intr_type type); | ||
131 | |||
132 | void mic_free_irq(struct mic_device *mdev, | ||
133 | struct mic_irq *cookie, void *data); | ||
134 | int mic_setup_interrupts(struct mic_device *mdev, struct pci_dev *pdev); | ||
135 | void mic_free_interrupts(struct mic_device *mdev, struct pci_dev *pdev); | ||
136 | void mic_intr_restore(struct mic_device *mdev); | ||
137 | #endif | ||
diff --git a/drivers/misc/mic/host/mic_main.c b/drivers/misc/mic/host/mic_main.c new file mode 100644 index 000000000000..b3520859abd3 --- /dev/null +++ b/drivers/misc/mic/host/mic_main.c | |||
@@ -0,0 +1,537 @@ | |||
1 | /* | ||
2 | * Intel MIC Platform Software Stack (MPSS) | ||
3 | * | ||
4 | * Copyright(c) 2013 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 Host driver. | ||
19 | * | ||
20 | * Global TODO's across the driver to be added after initial base | ||
21 | * patches are accepted upstream: | ||
22 | * 1) Enable DMA support. | ||
23 | * 2) Enable per vring interrupt support. | ||
24 | */ | ||
25 | #include <linux/fs.h> | ||
26 | #include <linux/module.h> | ||
27 | #include <linux/pci.h> | ||
28 | #include <linux/poll.h> | ||
29 | #include <linux/suspend.h> | ||
30 | |||
31 | #include <linux/mic_common.h> | ||
32 | #include "../common/mic_dev.h" | ||
33 | #include "mic_device.h" | ||
34 | #include "mic_x100.h" | ||
35 | #include "mic_smpt.h" | ||
36 | #include "mic_fops.h" | ||
37 | #include "mic_virtio.h" | ||
38 | |||
39 | static const char mic_driver_name[] = "mic"; | ||
40 | |||
41 | static DEFINE_PCI_DEVICE_TABLE(mic_pci_tbl) = { | ||
42 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2250)}, | ||
43 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2251)}, | ||
44 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2252)}, | ||
45 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2253)}, | ||
46 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2254)}, | ||
47 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2255)}, | ||
48 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2256)}, | ||
49 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2257)}, | ||
50 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2258)}, | ||
51 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2259)}, | ||
52 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_225a)}, | ||
53 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_225b)}, | ||
54 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_225c)}, | ||
55 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_225d)}, | ||
56 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_225e)}, | ||
57 | |||
58 | /* required last entry */ | ||
59 | { 0, } | ||
60 | }; | ||
61 | |||
62 | MODULE_DEVICE_TABLE(pci, mic_pci_tbl); | ||
63 | |||
64 | /* ID allocator for MIC devices */ | ||
65 | static struct ida g_mic_ida; | ||
66 | /* Class of MIC devices for sysfs accessibility. */ | ||
67 | static struct class *g_mic_class; | ||
68 | /* Base device node number for MIC devices */ | ||
69 | static dev_t g_mic_devno; | ||
70 | |||
71 | static const struct file_operations mic_fops = { | ||
72 | .open = mic_open, | ||
73 | .release = mic_release, | ||
74 | .unlocked_ioctl = mic_ioctl, | ||
75 | .poll = mic_poll, | ||
76 | .mmap = mic_mmap, | ||
77 | .owner = THIS_MODULE, | ||
78 | }; | ||
79 | |||
80 | /* Initialize the device page */ | ||
81 | static int mic_dp_init(struct mic_device *mdev) | ||
82 | { | ||
83 | mdev->dp = kzalloc(MIC_DP_SIZE, GFP_KERNEL); | ||
84 | if (!mdev->dp) { | ||
85 | dev_err(mdev->sdev->parent, "%s %d err %d\n", | ||
86 | __func__, __LINE__, -ENOMEM); | ||
87 | return -ENOMEM; | ||
88 | } | ||
89 | |||
90 | mdev->dp_dma_addr = mic_map_single(mdev, | ||
91 | mdev->dp, MIC_DP_SIZE); | ||
92 | if (mic_map_error(mdev->dp_dma_addr)) { | ||
93 | kfree(mdev->dp); | ||
94 | dev_err(mdev->sdev->parent, "%s %d err %d\n", | ||
95 | __func__, __LINE__, -ENOMEM); | ||
96 | return -ENOMEM; | ||
97 | } | ||
98 | mdev->ops->write_spad(mdev, MIC_DPLO_SPAD, mdev->dp_dma_addr); | ||
99 | mdev->ops->write_spad(mdev, MIC_DPHI_SPAD, mdev->dp_dma_addr >> 32); | ||
100 | return 0; | ||
101 | } | ||
102 | |||
103 | /* Uninitialize the device page */ | ||
104 | static void mic_dp_uninit(struct mic_device *mdev) | ||
105 | { | ||
106 | mic_unmap_single(mdev, mdev->dp_dma_addr, MIC_DP_SIZE); | ||
107 | kfree(mdev->dp); | ||
108 | } | ||
109 | |||
110 | /** | ||
111 | * mic_shutdown_db - Shutdown doorbell interrupt handler. | ||
112 | */ | ||
113 | static irqreturn_t mic_shutdown_db(int irq, void *data) | ||
114 | { | ||
115 | struct mic_device *mdev = data; | ||
116 | struct mic_bootparam *bootparam = mdev->dp; | ||
117 | |||
118 | mdev->ops->ack_interrupt(mdev); | ||
119 | |||
120 | switch (bootparam->shutdown_status) { | ||
121 | case MIC_HALTED: | ||
122 | case MIC_POWER_OFF: | ||
123 | case MIC_RESTART: | ||
124 | /* Fall through */ | ||
125 | case MIC_CRASHED: | ||
126 | schedule_work(&mdev->shutdown_work); | ||
127 | break; | ||
128 | default: | ||
129 | break; | ||
130 | }; | ||
131 | return IRQ_HANDLED; | ||
132 | } | ||
133 | |||
134 | /** | ||
135 | * mic_ops_init: Initialize HW specific operation tables. | ||
136 | * | ||
137 | * @mdev: pointer to mic_device instance | ||
138 | * | ||
139 | * returns none. | ||
140 | */ | ||
141 | static void mic_ops_init(struct mic_device *mdev) | ||
142 | { | ||
143 | switch (mdev->family) { | ||
144 | case MIC_FAMILY_X100: | ||
145 | mdev->ops = &mic_x100_ops; | ||
146 | mdev->intr_ops = &mic_x100_intr_ops; | ||
147 | mdev->smpt_ops = &mic_x100_smpt_ops; | ||
148 | break; | ||
149 | default: | ||
150 | break; | ||
151 | } | ||
152 | } | ||
153 | |||
154 | /** | ||
155 | * mic_get_family - Determine hardware family to which this MIC belongs. | ||
156 | * | ||
157 | * @pdev: The pci device structure | ||
158 | * | ||
159 | * returns family. | ||
160 | */ | ||
161 | static enum mic_hw_family mic_get_family(struct pci_dev *pdev) | ||
162 | { | ||
163 | enum mic_hw_family family; | ||
164 | |||
165 | switch (pdev->device) { | ||
166 | case MIC_X100_PCI_DEVICE_2250: | ||
167 | case MIC_X100_PCI_DEVICE_2251: | ||
168 | case MIC_X100_PCI_DEVICE_2252: | ||
169 | case MIC_X100_PCI_DEVICE_2253: | ||
170 | case MIC_X100_PCI_DEVICE_2254: | ||
171 | case MIC_X100_PCI_DEVICE_2255: | ||
172 | case MIC_X100_PCI_DEVICE_2256: | ||
173 | case MIC_X100_PCI_DEVICE_2257: | ||
174 | case MIC_X100_PCI_DEVICE_2258: | ||
175 | case MIC_X100_PCI_DEVICE_2259: | ||
176 | case MIC_X100_PCI_DEVICE_225a: | ||
177 | case MIC_X100_PCI_DEVICE_225b: | ||
178 | case MIC_X100_PCI_DEVICE_225c: | ||
179 | case MIC_X100_PCI_DEVICE_225d: | ||
180 | case MIC_X100_PCI_DEVICE_225e: | ||
181 | family = MIC_FAMILY_X100; | ||
182 | break; | ||
183 | default: | ||
184 | family = MIC_FAMILY_UNKNOWN; | ||
185 | break; | ||
186 | } | ||
187 | return family; | ||
188 | } | ||
189 | |||
190 | /** | ||
191 | * mic_pm_notifier: Notifier callback function that handles | ||
192 | * PM notifications. | ||
193 | * | ||
194 | * @notifier_block: The notifier structure. | ||
195 | * @pm_event: The event for which the driver was notified. | ||
196 | * @unused: Meaningless. Always NULL. | ||
197 | * | ||
198 | * returns NOTIFY_DONE | ||
199 | */ | ||
200 | static int mic_pm_notifier(struct notifier_block *notifier, | ||
201 | unsigned long pm_event, void *unused) | ||
202 | { | ||
203 | struct mic_device *mdev = container_of(notifier, | ||
204 | struct mic_device, pm_notifier); | ||
205 | |||
206 | switch (pm_event) { | ||
207 | case PM_HIBERNATION_PREPARE: | ||
208 | /* Fall through */ | ||
209 | case PM_SUSPEND_PREPARE: | ||
210 | mic_prepare_suspend(mdev); | ||
211 | break; | ||
212 | case PM_POST_HIBERNATION: | ||
213 | /* Fall through */ | ||
214 | case PM_POST_SUSPEND: | ||
215 | /* Fall through */ | ||
216 | case PM_POST_RESTORE: | ||
217 | mic_complete_resume(mdev); | ||
218 | break; | ||
219 | case PM_RESTORE_PREPARE: | ||
220 | break; | ||
221 | default: | ||
222 | break; | ||
223 | } | ||
224 | return NOTIFY_DONE; | ||
225 | } | ||
226 | |||
227 | /** | ||
228 | * mic_device_init - Allocates and initializes the MIC device structure | ||
229 | * | ||
230 | * @mdev: pointer to mic_device instance | ||
231 | * @pdev: The pci device structure | ||
232 | * | ||
233 | * returns none. | ||
234 | */ | ||
235 | static int | ||
236 | mic_device_init(struct mic_device *mdev, struct pci_dev *pdev) | ||
237 | { | ||
238 | int rc; | ||
239 | |||
240 | mdev->family = mic_get_family(pdev); | ||
241 | mdev->stepping = pdev->revision; | ||
242 | mic_ops_init(mdev); | ||
243 | mic_sysfs_init(mdev); | ||
244 | mutex_init(&mdev->mic_mutex); | ||
245 | mdev->irq_info.next_avail_src = 0; | ||
246 | INIT_WORK(&mdev->reset_trigger_work, mic_reset_trigger_work); | ||
247 | INIT_WORK(&mdev->shutdown_work, mic_shutdown_work); | ||
248 | init_completion(&mdev->reset_wait); | ||
249 | INIT_LIST_HEAD(&mdev->vdev_list); | ||
250 | mdev->pm_notifier.notifier_call = mic_pm_notifier; | ||
251 | rc = register_pm_notifier(&mdev->pm_notifier); | ||
252 | if (rc) { | ||
253 | dev_err(&pdev->dev, "register_pm_notifier failed rc %d\n", | ||
254 | rc); | ||
255 | goto register_pm_notifier_fail; | ||
256 | } | ||
257 | return 0; | ||
258 | register_pm_notifier_fail: | ||
259 | flush_work(&mdev->shutdown_work); | ||
260 | flush_work(&mdev->reset_trigger_work); | ||
261 | return rc; | ||
262 | } | ||
263 | |||
264 | /** | ||
265 | * mic_device_uninit - Frees resources allocated during mic_device_init(..) | ||
266 | * | ||
267 | * @mdev: pointer to mic_device instance | ||
268 | * | ||
269 | * returns none | ||
270 | */ | ||
271 | static void mic_device_uninit(struct mic_device *mdev) | ||
272 | { | ||
273 | /* The cmdline sysfs entry might have allocated cmdline */ | ||
274 | kfree(mdev->cmdline); | ||
275 | kfree(mdev->firmware); | ||
276 | kfree(mdev->ramdisk); | ||
277 | kfree(mdev->bootmode); | ||
278 | flush_work(&mdev->reset_trigger_work); | ||
279 | flush_work(&mdev->shutdown_work); | ||
280 | unregister_pm_notifier(&mdev->pm_notifier); | ||
281 | } | ||
282 | |||
283 | /** | ||
284 | * mic_probe - Device Initialization Routine | ||
285 | * | ||
286 | * @pdev: PCI device structure | ||
287 | * @ent: entry in mic_pci_tbl | ||
288 | * | ||
289 | * returns 0 on success, < 0 on failure. | ||
290 | */ | ||
291 | static int mic_probe(struct pci_dev *pdev, | ||
292 | const struct pci_device_id *ent) | ||
293 | { | ||
294 | int rc; | ||
295 | struct mic_device *mdev; | ||
296 | |||
297 | mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); | ||
298 | if (!mdev) { | ||
299 | rc = -ENOMEM; | ||
300 | dev_err(&pdev->dev, "mdev kmalloc failed rc %d\n", rc); | ||
301 | goto mdev_alloc_fail; | ||
302 | } | ||
303 | mdev->id = ida_simple_get(&g_mic_ida, 0, MIC_MAX_NUM_DEVS, GFP_KERNEL); | ||
304 | if (mdev->id < 0) { | ||
305 | rc = mdev->id; | ||
306 | dev_err(&pdev->dev, "ida_simple_get failed rc %d\n", rc); | ||
307 | goto ida_fail; | ||
308 | } | ||
309 | |||
310 | rc = mic_device_init(mdev, pdev); | ||
311 | if (rc) { | ||
312 | dev_err(&pdev->dev, "mic_device_init failed rc %d\n", rc); | ||
313 | goto device_init_fail; | ||
314 | } | ||
315 | |||
316 | rc = pci_enable_device(pdev); | ||
317 | if (rc) { | ||
318 | dev_err(&pdev->dev, "failed to enable pci device.\n"); | ||
319 | goto uninit_device; | ||
320 | } | ||
321 | |||
322 | pci_set_master(pdev); | ||
323 | |||
324 | rc = pci_request_regions(pdev, mic_driver_name); | ||
325 | if (rc) { | ||
326 | dev_err(&pdev->dev, "failed to get pci regions.\n"); | ||
327 | goto disable_device; | ||
328 | } | ||
329 | |||
330 | rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(64)); | ||
331 | if (rc) { | ||
332 | dev_err(&pdev->dev, "Cannot set DMA mask\n"); | ||
333 | goto release_regions; | ||
334 | } | ||
335 | |||
336 | mdev->mmio.pa = pci_resource_start(pdev, mdev->ops->mmio_bar); | ||
337 | mdev->mmio.len = pci_resource_len(pdev, mdev->ops->mmio_bar); | ||
338 | mdev->mmio.va = pci_ioremap_bar(pdev, mdev->ops->mmio_bar); | ||
339 | if (!mdev->mmio.va) { | ||
340 | dev_err(&pdev->dev, "Cannot remap MMIO BAR\n"); | ||
341 | rc = -EIO; | ||
342 | goto release_regions; | ||
343 | } | ||
344 | |||
345 | mdev->aper.pa = pci_resource_start(pdev, mdev->ops->aper_bar); | ||
346 | mdev->aper.len = pci_resource_len(pdev, mdev->ops->aper_bar); | ||
347 | mdev->aper.va = ioremap_wc(mdev->aper.pa, mdev->aper.len); | ||
348 | if (!mdev->aper.va) { | ||
349 | dev_err(&pdev->dev, "Cannot remap Aperture BAR\n"); | ||
350 | rc = -EIO; | ||
351 | goto unmap_mmio; | ||
352 | } | ||
353 | |||
354 | mdev->intr_ops->intr_init(mdev); | ||
355 | rc = mic_setup_interrupts(mdev, pdev); | ||
356 | if (rc) { | ||
357 | dev_err(&pdev->dev, "mic_setup_interrupts failed %d\n", rc); | ||
358 | goto unmap_aper; | ||
359 | } | ||
360 | rc = mic_smpt_init(mdev); | ||
361 | if (rc) { | ||
362 | dev_err(&pdev->dev, "smpt_init failed %d\n", rc); | ||
363 | goto free_interrupts; | ||
364 | } | ||
365 | |||
366 | pci_set_drvdata(pdev, mdev); | ||
367 | |||
368 | mdev->sdev = device_create_with_groups(g_mic_class, &pdev->dev, | ||
369 | MKDEV(MAJOR(g_mic_devno), mdev->id), NULL, | ||
370 | mdev->attr_group, "mic%d", mdev->id); | ||
371 | if (IS_ERR(mdev->sdev)) { | ||
372 | rc = PTR_ERR(mdev->sdev); | ||
373 | dev_err(&pdev->dev, | ||
374 | "device_create_with_groups failed rc %d\n", rc); | ||
375 | goto smpt_uninit; | ||
376 | } | ||
377 | mdev->state_sysfs = sysfs_get_dirent(mdev->sdev->kobj.sd, | ||
378 | NULL, "state"); | ||
379 | if (!mdev->state_sysfs) { | ||
380 | rc = -ENODEV; | ||
381 | dev_err(&pdev->dev, "sysfs_get_dirent failed rc %d\n", rc); | ||
382 | goto destroy_device; | ||
383 | } | ||
384 | |||
385 | rc = mic_dp_init(mdev); | ||
386 | if (rc) { | ||
387 | dev_err(&pdev->dev, "mic_dp_init failed rc %d\n", rc); | ||
388 | goto sysfs_put; | ||
389 | } | ||
390 | mutex_lock(&mdev->mic_mutex); | ||
391 | |||
392 | mdev->shutdown_db = mic_next_db(mdev); | ||
393 | mdev->shutdown_cookie = mic_request_irq(mdev, mic_shutdown_db, | ||
394 | "shutdown-interrupt", mdev, mdev->shutdown_db, MIC_INTR_DB); | ||
395 | if (IS_ERR(mdev->shutdown_cookie)) { | ||
396 | rc = PTR_ERR(mdev->shutdown_cookie); | ||
397 | mutex_unlock(&mdev->mic_mutex); | ||
398 | goto dp_uninit; | ||
399 | } | ||
400 | mutex_unlock(&mdev->mic_mutex); | ||
401 | mic_bootparam_init(mdev); | ||
402 | |||
403 | mic_create_debug_dir(mdev); | ||
404 | cdev_init(&mdev->cdev, &mic_fops); | ||
405 | mdev->cdev.owner = THIS_MODULE; | ||
406 | rc = cdev_add(&mdev->cdev, MKDEV(MAJOR(g_mic_devno), mdev->id), 1); | ||
407 | if (rc) { | ||
408 | dev_err(&pdev->dev, "cdev_add err id %d rc %d\n", mdev->id, rc); | ||
409 | goto cleanup_debug_dir; | ||
410 | } | ||
411 | return 0; | ||
412 | cleanup_debug_dir: | ||
413 | mic_delete_debug_dir(mdev); | ||
414 | mutex_lock(&mdev->mic_mutex); | ||
415 | mic_free_irq(mdev, mdev->shutdown_cookie, mdev); | ||
416 | mutex_unlock(&mdev->mic_mutex); | ||
417 | dp_uninit: | ||
418 | mic_dp_uninit(mdev); | ||
419 | sysfs_put: | ||
420 | sysfs_put(mdev->state_sysfs); | ||
421 | destroy_device: | ||
422 | device_destroy(g_mic_class, MKDEV(MAJOR(g_mic_devno), mdev->id)); | ||
423 | smpt_uninit: | ||
424 | mic_smpt_uninit(mdev); | ||
425 | free_interrupts: | ||
426 | mic_free_interrupts(mdev, pdev); | ||
427 | unmap_aper: | ||
428 | iounmap(mdev->aper.va); | ||
429 | unmap_mmio: | ||
430 | iounmap(mdev->mmio.va); | ||
431 | release_regions: | ||
432 | pci_release_regions(pdev); | ||
433 | disable_device: | ||
434 | pci_disable_device(pdev); | ||
435 | uninit_device: | ||
436 | mic_device_uninit(mdev); | ||
437 | device_init_fail: | ||
438 | ida_simple_remove(&g_mic_ida, mdev->id); | ||
439 | ida_fail: | ||
440 | kfree(mdev); | ||
441 | mdev_alloc_fail: | ||
442 | dev_err(&pdev->dev, "Probe failed rc %d\n", rc); | ||
443 | return rc; | ||
444 | } | ||
445 | |||
446 | /** | ||
447 | * mic_remove - Device Removal Routine | ||
448 | * mic_remove is called by the PCI subsystem to alert the driver | ||
449 | * that it should release a PCI device. | ||
450 | * | ||
451 | * @pdev: PCI device structure | ||
452 | */ | ||
453 | static void mic_remove(struct pci_dev *pdev) | ||
454 | { | ||
455 | struct mic_device *mdev; | ||
456 | |||
457 | mdev = pci_get_drvdata(pdev); | ||
458 | if (!mdev) | ||
459 | return; | ||
460 | |||
461 | mic_stop(mdev, false); | ||
462 | cdev_del(&mdev->cdev); | ||
463 | mic_delete_debug_dir(mdev); | ||
464 | mutex_lock(&mdev->mic_mutex); | ||
465 | mic_free_irq(mdev, mdev->shutdown_cookie, mdev); | ||
466 | mutex_unlock(&mdev->mic_mutex); | ||
467 | flush_work(&mdev->shutdown_work); | ||
468 | mic_dp_uninit(mdev); | ||
469 | sysfs_put(mdev->state_sysfs); | ||
470 | device_destroy(g_mic_class, MKDEV(MAJOR(g_mic_devno), mdev->id)); | ||
471 | mic_smpt_uninit(mdev); | ||
472 | mic_free_interrupts(mdev, pdev); | ||
473 | iounmap(mdev->mmio.va); | ||
474 | iounmap(mdev->aper.va); | ||
475 | mic_device_uninit(mdev); | ||
476 | pci_release_regions(pdev); | ||
477 | pci_disable_device(pdev); | ||
478 | ida_simple_remove(&g_mic_ida, mdev->id); | ||
479 | kfree(mdev); | ||
480 | } | ||
481 | static struct pci_driver mic_driver = { | ||
482 | .name = mic_driver_name, | ||
483 | .id_table = mic_pci_tbl, | ||
484 | .probe = mic_probe, | ||
485 | .remove = mic_remove | ||
486 | }; | ||
487 | |||
488 | static int __init mic_init(void) | ||
489 | { | ||
490 | int ret; | ||
491 | |||
492 | ret = alloc_chrdev_region(&g_mic_devno, 0, | ||
493 | MIC_MAX_NUM_DEVS, mic_driver_name); | ||
494 | if (ret) { | ||
495 | pr_err("alloc_chrdev_region failed ret %d\n", ret); | ||
496 | goto error; | ||
497 | } | ||
498 | |||
499 | g_mic_class = class_create(THIS_MODULE, mic_driver_name); | ||
500 | if (IS_ERR(g_mic_class)) { | ||
501 | ret = PTR_ERR(g_mic_class); | ||
502 | pr_err("class_create failed ret %d\n", ret); | ||
503 | goto cleanup_chrdev; | ||
504 | } | ||
505 | |||
506 | mic_init_debugfs(); | ||
507 | ida_init(&g_mic_ida); | ||
508 | ret = pci_register_driver(&mic_driver); | ||
509 | if (ret) { | ||
510 | pr_err("pci_register_driver failed ret %d\n", ret); | ||
511 | goto cleanup_debugfs; | ||
512 | } | ||
513 | return ret; | ||
514 | cleanup_debugfs: | ||
515 | mic_exit_debugfs(); | ||
516 | class_destroy(g_mic_class); | ||
517 | cleanup_chrdev: | ||
518 | unregister_chrdev_region(g_mic_devno, MIC_MAX_NUM_DEVS); | ||
519 | error: | ||
520 | return ret; | ||
521 | } | ||
522 | |||
523 | static void __exit mic_exit(void) | ||
524 | { | ||
525 | pci_unregister_driver(&mic_driver); | ||
526 | ida_destroy(&g_mic_ida); | ||
527 | mic_exit_debugfs(); | ||
528 | class_destroy(g_mic_class); | ||
529 | unregister_chrdev_region(g_mic_devno, MIC_MAX_NUM_DEVS); | ||
530 | } | ||
531 | |||
532 | module_init(mic_init); | ||
533 | module_exit(mic_exit); | ||
534 | |||
535 | MODULE_AUTHOR("Intel Corporation"); | ||
536 | MODULE_DESCRIPTION("Intel(R) MIC X100 Host driver"); | ||
537 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/misc/mic/host/mic_smpt.c b/drivers/misc/mic/host/mic_smpt.c new file mode 100644 index 000000000000..fae474c4899e --- /dev/null +++ b/drivers/misc/mic/host/mic_smpt.c | |||
@@ -0,0 +1,442 @@ | |||
1 | /* | ||
2 | * Intel MIC Platform Software Stack (MPSS) | ||
3 | * | ||
4 | * Copyright(c) 2013 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 Host driver. | ||
19 | * | ||
20 | */ | ||
21 | #include <linux/pci.h> | ||
22 | |||
23 | #include "../common/mic_dev.h" | ||
24 | #include "mic_device.h" | ||
25 | #include "mic_smpt.h" | ||
26 | |||
27 | static inline u64 mic_system_page_mask(struct mic_device *mdev) | ||
28 | { | ||
29 | return (1ULL << mdev->smpt->info.page_shift) - 1ULL; | ||
30 | } | ||
31 | |||
32 | static inline u8 mic_sys_addr_to_smpt(struct mic_device *mdev, dma_addr_t pa) | ||
33 | { | ||
34 | return (pa - mdev->smpt->info.base) >> mdev->smpt->info.page_shift; | ||
35 | } | ||
36 | |||
37 | static inline u64 mic_smpt_to_pa(struct mic_device *mdev, u8 index) | ||
38 | { | ||
39 | return mdev->smpt->info.base + (index * mdev->smpt->info.page_size); | ||
40 | } | ||
41 | |||
42 | static inline u64 mic_smpt_offset(struct mic_device *mdev, dma_addr_t pa) | ||
43 | { | ||
44 | return pa & mic_system_page_mask(mdev); | ||
45 | } | ||
46 | |||
47 | static inline u64 mic_smpt_align_low(struct mic_device *mdev, dma_addr_t pa) | ||
48 | { | ||
49 | return ALIGN(pa - mic_system_page_mask(mdev), | ||
50 | mdev->smpt->info.page_size); | ||
51 | } | ||
52 | |||
53 | static inline u64 mic_smpt_align_high(struct mic_device *mdev, dma_addr_t pa) | ||
54 | { | ||
55 | return ALIGN(pa, mdev->smpt->info.page_size); | ||
56 | } | ||
57 | |||
58 | /* Total Cumulative system memory accessible by MIC across all SMPT entries */ | ||
59 | static inline u64 mic_max_system_memory(struct mic_device *mdev) | ||
60 | { | ||
61 | return mdev->smpt->info.num_reg * mdev->smpt->info.page_size; | ||
62 | } | ||
63 | |||
64 | /* Maximum system memory address accessible by MIC */ | ||
65 | static inline u64 mic_max_system_addr(struct mic_device *mdev) | ||
66 | { | ||
67 | return mdev->smpt->info.base + mic_max_system_memory(mdev) - 1ULL; | ||
68 | } | ||
69 | |||
70 | /* Check if the DMA address is a MIC system memory address */ | ||
71 | static inline bool | ||
72 | mic_is_system_addr(struct mic_device *mdev, dma_addr_t pa) | ||
73 | { | ||
74 | return pa >= mdev->smpt->info.base && pa <= mic_max_system_addr(mdev); | ||
75 | } | ||
76 | |||
77 | /* Populate an SMPT entry and update the reference counts. */ | ||
78 | static void mic_add_smpt_entry(int spt, s64 *ref, u64 addr, | ||
79 | int entries, struct mic_device *mdev) | ||
80 | { | ||
81 | struct mic_smpt_info *smpt_info = mdev->smpt; | ||
82 | int i; | ||
83 | |||
84 | for (i = spt; i < spt + entries; i++, | ||
85 | addr += smpt_info->info.page_size) { | ||
86 | if (!smpt_info->entry[i].ref_count && | ||
87 | (smpt_info->entry[i].dma_addr != addr)) { | ||
88 | mdev->smpt_ops->set(mdev, addr, i); | ||
89 | smpt_info->entry[i].dma_addr = addr; | ||
90 | } | ||
91 | smpt_info->entry[i].ref_count += ref[i - spt]; | ||
92 | } | ||
93 | } | ||
94 | |||
95 | /* | ||
96 | * Find an available MIC address in MIC SMPT address space | ||
97 | * for a given DMA address and size. | ||
98 | */ | ||
99 | static dma_addr_t mic_smpt_op(struct mic_device *mdev, u64 dma_addr, | ||
100 | int entries, s64 *ref, size_t size) | ||
101 | { | ||
102 | int spt; | ||
103 | int ae = 0; | ||
104 | int i; | ||
105 | unsigned long flags; | ||
106 | dma_addr_t mic_addr = 0; | ||
107 | dma_addr_t addr = dma_addr; | ||
108 | struct mic_smpt_info *smpt_info = mdev->smpt; | ||
109 | |||
110 | spin_lock_irqsave(&smpt_info->smpt_lock, flags); | ||
111 | |||
112 | /* find existing entries */ | ||
113 | for (i = 0; i < smpt_info->info.num_reg; i++) { | ||
114 | if (smpt_info->entry[i].dma_addr == addr) { | ||
115 | ae++; | ||
116 | addr += smpt_info->info.page_size; | ||
117 | } else if (ae) /* cannot find contiguous entries */ | ||
118 | goto not_found; | ||
119 | |||
120 | if (ae == entries) | ||
121 | goto found; | ||
122 | } | ||
123 | |||
124 | /* find free entry */ | ||
125 | for (ae = 0, i = 0; i < smpt_info->info.num_reg; i++) { | ||
126 | ae = (smpt_info->entry[i].ref_count == 0) ? ae + 1 : 0; | ||
127 | if (ae == entries) | ||
128 | goto found; | ||
129 | } | ||
130 | |||
131 | not_found: | ||
132 | spin_unlock_irqrestore(&smpt_info->smpt_lock, flags); | ||
133 | return mic_addr; | ||
134 | |||
135 | found: | ||
136 | spt = i - entries + 1; | ||
137 | mic_addr = mic_smpt_to_pa(mdev, spt); | ||
138 | mic_add_smpt_entry(spt, ref, dma_addr, entries, mdev); | ||
139 | smpt_info->map_count++; | ||
140 | smpt_info->ref_count += (s64)size; | ||
141 | spin_unlock_irqrestore(&smpt_info->smpt_lock, flags); | ||
142 | return mic_addr; | ||
143 | } | ||
144 | |||
145 | /* | ||
146 | * Returns number of smpt entries needed for dma_addr to dma_addr + size | ||
147 | * also returns the reference count array for each of those entries | ||
148 | * and the starting smpt address | ||
149 | */ | ||
150 | static int mic_get_smpt_ref_count(struct mic_device *mdev, dma_addr_t dma_addr, | ||
151 | size_t size, s64 *ref, u64 *smpt_start) | ||
152 | { | ||
153 | u64 start = dma_addr; | ||
154 | u64 end = dma_addr + size; | ||
155 | int i = 0; | ||
156 | |||
157 | while (start < end) { | ||
158 | ref[i++] = min(mic_smpt_align_high(mdev, start + 1), | ||
159 | end) - start; | ||
160 | start = mic_smpt_align_high(mdev, start + 1); | ||
161 | } | ||
162 | |||
163 | if (smpt_start) | ||
164 | *smpt_start = mic_smpt_align_low(mdev, dma_addr); | ||
165 | |||
166 | return i; | ||
167 | } | ||
168 | |||
169 | /* | ||
170 | * mic_to_dma_addr - Converts a MIC address to a DMA address. | ||
171 | * | ||
172 | * @mdev: pointer to mic_device instance. | ||
173 | * @mic_addr: MIC address. | ||
174 | * | ||
175 | * returns a DMA address. | ||
176 | */ | ||
177 | static dma_addr_t | ||
178 | mic_to_dma_addr(struct mic_device *mdev, dma_addr_t mic_addr) | ||
179 | { | ||
180 | struct mic_smpt_info *smpt_info = mdev->smpt; | ||
181 | int spt; | ||
182 | dma_addr_t dma_addr; | ||
183 | |||
184 | if (!mic_is_system_addr(mdev, mic_addr)) { | ||
185 | dev_err(mdev->sdev->parent, | ||
186 | "mic_addr is invalid. mic_addr = 0x%llx\n", mic_addr); | ||
187 | return -EINVAL; | ||
188 | } | ||
189 | spt = mic_sys_addr_to_smpt(mdev, mic_addr); | ||
190 | dma_addr = smpt_info->entry[spt].dma_addr + | ||
191 | mic_smpt_offset(mdev, mic_addr); | ||
192 | return dma_addr; | ||
193 | } | ||
194 | |||
195 | /** | ||
196 | * mic_map - Maps a DMA address to a MIC physical address. | ||
197 | * | ||
198 | * @mdev: pointer to mic_device instance. | ||
199 | * @dma_addr: DMA address. | ||
200 | * @size: Size of the region to be mapped. | ||
201 | * | ||
202 | * This API converts the DMA address provided to a DMA address understood | ||
203 | * by MIC. Caller should check for errors by calling mic_map_error(..). | ||
204 | * | ||
205 | * returns DMA address as required by MIC. | ||
206 | */ | ||
207 | dma_addr_t mic_map(struct mic_device *mdev, dma_addr_t dma_addr, size_t size) | ||
208 | { | ||
209 | dma_addr_t mic_addr = 0; | ||
210 | int num_entries; | ||
211 | s64 *ref; | ||
212 | u64 smpt_start; | ||
213 | |||
214 | if (!size || size > mic_max_system_memory(mdev)) | ||
215 | return mic_addr; | ||
216 | |||
217 | ref = kmalloc(mdev->smpt->info.num_reg * sizeof(s64), GFP_KERNEL); | ||
218 | if (!ref) | ||
219 | return mic_addr; | ||
220 | |||
221 | num_entries = mic_get_smpt_ref_count(mdev, dma_addr, size, | ||
222 | ref, &smpt_start); | ||
223 | |||
224 | /* Set the smpt table appropriately and get 16G aligned mic address */ | ||
225 | mic_addr = mic_smpt_op(mdev, smpt_start, num_entries, ref, size); | ||
226 | |||
227 | kfree(ref); | ||
228 | |||
229 | /* | ||
230 | * If mic_addr is zero then its an error case | ||
231 | * since mic_addr can never be zero. | ||
232 | * else generate mic_addr by adding the 16G offset in dma_addr | ||
233 | */ | ||
234 | if (!mic_addr && MIC_FAMILY_X100 == mdev->family) { | ||
235 | dev_err(mdev->sdev->parent, | ||
236 | "mic_map failed dma_addr 0x%llx size 0x%lx\n", | ||
237 | dma_addr, size); | ||
238 | return mic_addr; | ||
239 | } else { | ||
240 | return mic_addr + mic_smpt_offset(mdev, dma_addr); | ||
241 | } | ||
242 | } | ||
243 | |||
244 | /** | ||
245 | * mic_unmap - Unmaps a MIC physical address. | ||
246 | * | ||
247 | * @mdev: pointer to mic_device instance. | ||
248 | * @mic_addr: MIC physical address. | ||
249 | * @size: Size of the region to be unmapped. | ||
250 | * | ||
251 | * This API unmaps the mappings created by mic_map(..). | ||
252 | * | ||
253 | * returns None. | ||
254 | */ | ||
255 | void mic_unmap(struct mic_device *mdev, dma_addr_t mic_addr, size_t size) | ||
256 | { | ||
257 | struct mic_smpt_info *smpt_info = mdev->smpt; | ||
258 | s64 *ref; | ||
259 | int num_smpt; | ||
260 | int spt; | ||
261 | int i; | ||
262 | unsigned long flags; | ||
263 | |||
264 | if (!size) | ||
265 | return; | ||
266 | |||
267 | if (!mic_is_system_addr(mdev, mic_addr)) { | ||
268 | dev_err(mdev->sdev->parent, | ||
269 | "invalid address: 0x%llx\n", mic_addr); | ||
270 | return; | ||
271 | } | ||
272 | |||
273 | spt = mic_sys_addr_to_smpt(mdev, mic_addr); | ||
274 | ref = kmalloc(mdev->smpt->info.num_reg * sizeof(s64), GFP_KERNEL); | ||
275 | if (!ref) | ||
276 | return; | ||
277 | |||
278 | /* Get number of smpt entries to be mapped, ref count array */ | ||
279 | num_smpt = mic_get_smpt_ref_count(mdev, mic_addr, size, ref, NULL); | ||
280 | |||
281 | spin_lock_irqsave(&smpt_info->smpt_lock, flags); | ||
282 | smpt_info->unmap_count++; | ||
283 | smpt_info->ref_count -= (s64)size; | ||
284 | |||
285 | for (i = spt; i < spt + num_smpt; i++) { | ||
286 | smpt_info->entry[i].ref_count -= ref[i - spt]; | ||
287 | if (smpt_info->entry[i].ref_count < 0) | ||
288 | dev_warn(mdev->sdev->parent, | ||
289 | "ref count for entry %d is negative\n", i); | ||
290 | } | ||
291 | spin_unlock_irqrestore(&smpt_info->smpt_lock, flags); | ||
292 | kfree(ref); | ||
293 | } | ||
294 | |||
295 | /** | ||
296 | * mic_map_single - Maps a virtual address to a MIC physical address. | ||
297 | * | ||
298 | * @mdev: pointer to mic_device instance. | ||
299 | * @va: Kernel direct mapped virtual address. | ||
300 | * @size: Size of the region to be mapped. | ||
301 | * | ||
302 | * This API calls pci_map_single(..) for the direct mapped virtual address | ||
303 | * and then converts the DMA address provided to a DMA address understood | ||
304 | * by MIC. Caller should check for errors by calling mic_map_error(..). | ||
305 | * | ||
306 | * returns DMA address as required by MIC. | ||
307 | */ | ||
308 | dma_addr_t mic_map_single(struct mic_device *mdev, void *va, size_t size) | ||
309 | { | ||
310 | dma_addr_t mic_addr = 0; | ||
311 | struct pci_dev *pdev = container_of(mdev->sdev->parent, | ||
312 | struct pci_dev, dev); | ||
313 | dma_addr_t dma_addr = | ||
314 | pci_map_single(pdev, va, size, PCI_DMA_BIDIRECTIONAL); | ||
315 | |||
316 | if (!pci_dma_mapping_error(pdev, dma_addr)) { | ||
317 | mic_addr = mic_map(mdev, dma_addr, size); | ||
318 | if (!mic_addr) { | ||
319 | dev_err(mdev->sdev->parent, | ||
320 | "mic_map failed dma_addr 0x%llx size 0x%lx\n", | ||
321 | dma_addr, size); | ||
322 | pci_unmap_single(pdev, dma_addr, | ||
323 | size, PCI_DMA_BIDIRECTIONAL); | ||
324 | } | ||
325 | } | ||
326 | return mic_addr; | ||
327 | } | ||
328 | |||
329 | /** | ||
330 | * mic_unmap_single - Unmaps a MIC physical address. | ||
331 | * | ||
332 | * @mdev: pointer to mic_device instance. | ||
333 | * @mic_addr: MIC physical address. | ||
334 | * @size: Size of the region to be unmapped. | ||
335 | * | ||
336 | * This API unmaps the mappings created by mic_map_single(..). | ||
337 | * | ||
338 | * returns None. | ||
339 | */ | ||
340 | void | ||
341 | mic_unmap_single(struct mic_device *mdev, dma_addr_t mic_addr, size_t size) | ||
342 | { | ||
343 | struct pci_dev *pdev = container_of(mdev->sdev->parent, | ||
344 | struct pci_dev, dev); | ||
345 | dma_addr_t dma_addr = mic_to_dma_addr(mdev, mic_addr); | ||
346 | mic_unmap(mdev, mic_addr, size); | ||
347 | pci_unmap_single(pdev, dma_addr, size, PCI_DMA_BIDIRECTIONAL); | ||
348 | } | ||
349 | |||
350 | /** | ||
351 | * mic_smpt_init - Initialize MIC System Memory Page Tables. | ||
352 | * | ||
353 | * @mdev: pointer to mic_device instance. | ||
354 | * | ||
355 | * returns 0 for success and -errno for error. | ||
356 | */ | ||
357 | int mic_smpt_init(struct mic_device *mdev) | ||
358 | { | ||
359 | int i, err = 0; | ||
360 | dma_addr_t dma_addr; | ||
361 | struct mic_smpt_info *smpt_info; | ||
362 | |||
363 | mdev->smpt = kmalloc(sizeof(*mdev->smpt), GFP_KERNEL); | ||
364 | if (!mdev->smpt) | ||
365 | return -ENOMEM; | ||
366 | |||
367 | smpt_info = mdev->smpt; | ||
368 | mdev->smpt_ops->init(mdev); | ||
369 | smpt_info->entry = kmalloc_array(smpt_info->info.num_reg, | ||
370 | sizeof(*smpt_info->entry), GFP_KERNEL); | ||
371 | if (!smpt_info->entry) { | ||
372 | err = -ENOMEM; | ||
373 | goto free_smpt; | ||
374 | } | ||
375 | spin_lock_init(&smpt_info->smpt_lock); | ||
376 | for (i = 0; i < smpt_info->info.num_reg; i++) { | ||
377 | dma_addr = i * smpt_info->info.page_size; | ||
378 | smpt_info->entry[i].dma_addr = dma_addr; | ||
379 | smpt_info->entry[i].ref_count = 0; | ||
380 | mdev->smpt_ops->set(mdev, dma_addr, i); | ||
381 | } | ||
382 | smpt_info->ref_count = 0; | ||
383 | smpt_info->map_count = 0; | ||
384 | smpt_info->unmap_count = 0; | ||
385 | return 0; | ||
386 | free_smpt: | ||
387 | kfree(smpt_info); | ||
388 | return err; | ||
389 | } | ||
390 | |||
391 | /** | ||
392 | * mic_smpt_uninit - UnInitialize MIC System Memory Page Tables. | ||
393 | * | ||
394 | * @mdev: pointer to mic_device instance. | ||
395 | * | ||
396 | * returns None. | ||
397 | */ | ||
398 | void mic_smpt_uninit(struct mic_device *mdev) | ||
399 | { | ||
400 | struct mic_smpt_info *smpt_info = mdev->smpt; | ||
401 | int i; | ||
402 | |||
403 | dev_dbg(mdev->sdev->parent, | ||
404 | "nodeid %d SMPT ref count %lld map %lld unmap %lld\n", | ||
405 | mdev->id, smpt_info->ref_count, | ||
406 | smpt_info->map_count, smpt_info->unmap_count); | ||
407 | |||
408 | for (i = 0; i < smpt_info->info.num_reg; i++) { | ||
409 | dev_dbg(mdev->sdev->parent, | ||
410 | "SMPT entry[%d] dma_addr = 0x%llx ref_count = %lld\n", | ||
411 | i, smpt_info->entry[i].dma_addr, | ||
412 | smpt_info->entry[i].ref_count); | ||
413 | if (smpt_info->entry[i].ref_count) | ||
414 | dev_warn(mdev->sdev->parent, | ||
415 | "ref count for entry %d is not zero\n", i); | ||
416 | } | ||
417 | kfree(smpt_info->entry); | ||
418 | kfree(smpt_info); | ||
419 | } | ||
420 | |||
421 | /** | ||
422 | * mic_smpt_restore - Restore MIC System Memory Page Tables. | ||
423 | * | ||
424 | * @mdev: pointer to mic_device instance. | ||
425 | * | ||
426 | * Restore the SMPT registers to values previously stored in the | ||
427 | * SW data structures. Some MIC steppings lose register state | ||
428 | * across resets and this API should be called for performing | ||
429 | * a restore operation if required. | ||
430 | * | ||
431 | * returns None. | ||
432 | */ | ||
433 | void mic_smpt_restore(struct mic_device *mdev) | ||
434 | { | ||
435 | int i; | ||
436 | dma_addr_t dma_addr; | ||
437 | |||
438 | for (i = 0; i < mdev->smpt->info.num_reg; i++) { | ||
439 | dma_addr = mdev->smpt->entry[i].dma_addr; | ||
440 | mdev->smpt_ops->set(mdev, dma_addr, i); | ||
441 | } | ||
442 | } | ||
diff --git a/drivers/misc/mic/host/mic_smpt.h b/drivers/misc/mic/host/mic_smpt.h new file mode 100644 index 000000000000..51970abfe7df --- /dev/null +++ b/drivers/misc/mic/host/mic_smpt.h | |||
@@ -0,0 +1,98 @@ | |||
1 | /* | ||
2 | * Intel MIC Platform Software Stack (MPSS) | ||
3 | * | ||
4 | * Copyright(c) 2013 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 Host driver. | ||
19 | * | ||
20 | */ | ||
21 | #ifndef MIC_SMPT_H | ||
22 | #define MIC_SMPT_H | ||
23 | /** | ||
24 | * struct mic_smpt_ops - MIC HW specific SMPT operations. | ||
25 | * @init: Initialize hardware specific SMPT information in mic_smpt_hw_info. | ||
26 | * @set: Set the value for a particular SMPT entry. | ||
27 | */ | ||
28 | struct mic_smpt_ops { | ||
29 | void (*init)(struct mic_device *mdev); | ||
30 | void (*set)(struct mic_device *mdev, dma_addr_t dma_addr, u8 index); | ||
31 | }; | ||
32 | |||
33 | /** | ||
34 | * struct mic_smpt - MIC SMPT entry information. | ||
35 | * @dma_addr: Base DMA address for this SMPT entry. | ||
36 | * @ref_count: Number of active mappings for this SMPT entry in bytes. | ||
37 | */ | ||
38 | struct mic_smpt { | ||
39 | dma_addr_t dma_addr; | ||
40 | s64 ref_count; | ||
41 | }; | ||
42 | |||
43 | /** | ||
44 | * struct mic_smpt_hw_info - MIC SMPT hardware specific information. | ||
45 | * @num_reg: Number of SMPT registers. | ||
46 | * @page_shift: System memory page shift. | ||
47 | * @page_size: System memory page size. | ||
48 | * @base: System address base. | ||
49 | */ | ||
50 | struct mic_smpt_hw_info { | ||
51 | u8 num_reg; | ||
52 | u8 page_shift; | ||
53 | u64 page_size; | ||
54 | u64 base; | ||
55 | }; | ||
56 | |||
57 | /** | ||
58 | * struct mic_smpt_info - MIC SMPT information. | ||
59 | * @entry: Array of SMPT entries. | ||
60 | * @smpt_lock: Spin lock protecting access to SMPT data structures. | ||
61 | * @info: Hardware specific SMPT information. | ||
62 | * @ref_count: Number of active SMPT mappings (for debug). | ||
63 | * @map_count: Number of SMPT mappings created (for debug). | ||
64 | * @unmap_count: Number of SMPT mappings destroyed (for debug). | ||
65 | */ | ||
66 | struct mic_smpt_info { | ||
67 | struct mic_smpt *entry; | ||
68 | spinlock_t smpt_lock; | ||
69 | struct mic_smpt_hw_info info; | ||
70 | s64 ref_count; | ||
71 | s64 map_count; | ||
72 | s64 unmap_count; | ||
73 | }; | ||
74 | |||
75 | dma_addr_t mic_map_single(struct mic_device *mdev, void *va, size_t size); | ||
76 | void mic_unmap_single(struct mic_device *mdev, | ||
77 | dma_addr_t mic_addr, size_t size); | ||
78 | dma_addr_t mic_map(struct mic_device *mdev, | ||
79 | dma_addr_t dma_addr, size_t size); | ||
80 | void mic_unmap(struct mic_device *mdev, dma_addr_t mic_addr, size_t size); | ||
81 | |||
82 | /** | ||
83 | * mic_map_error - Check a MIC address for errors. | ||
84 | * | ||
85 | * @mdev: pointer to mic_device instance. | ||
86 | * | ||
87 | * returns Whether there was an error during mic_map..(..) APIs. | ||
88 | */ | ||
89 | static inline bool mic_map_error(dma_addr_t mic_addr) | ||
90 | { | ||
91 | return !mic_addr; | ||
92 | } | ||
93 | |||
94 | int mic_smpt_init(struct mic_device *mdev); | ||
95 | void mic_smpt_uninit(struct mic_device *mdev); | ||
96 | void mic_smpt_restore(struct mic_device *mdev); | ||
97 | |||
98 | #endif | ||
diff --git a/drivers/misc/mic/host/mic_sysfs.c b/drivers/misc/mic/host/mic_sysfs.c new file mode 100644 index 000000000000..6dd864e4a617 --- /dev/null +++ b/drivers/misc/mic/host/mic_sysfs.c | |||
@@ -0,0 +1,459 @@ | |||
1 | /* | ||
2 | * Intel MIC Platform Software Stack (MPSS) | ||
3 | * | ||
4 | * Copyright(c) 2013 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 Host driver. | ||
19 | * | ||
20 | */ | ||
21 | #include <linux/pci.h> | ||
22 | |||
23 | #include <linux/mic_common.h> | ||
24 | #include "../common/mic_dev.h" | ||
25 | #include "mic_device.h" | ||
26 | |||
27 | /* | ||
28 | * A state-to-string lookup table, for exposing a human readable state | ||
29 | * via sysfs. Always keep in sync with enum mic_states | ||
30 | */ | ||
31 | static const char * const mic_state_string[] = { | ||
32 | [MIC_OFFLINE] = "offline", | ||
33 | [MIC_ONLINE] = "online", | ||
34 | [MIC_SHUTTING_DOWN] = "shutting_down", | ||
35 | [MIC_RESET_FAILED] = "reset_failed", | ||
36 | [MIC_SUSPENDING] = "suspending", | ||
37 | [MIC_SUSPENDED] = "suspended", | ||
38 | }; | ||
39 | |||
40 | /* | ||
41 | * A shutdown-status-to-string lookup table, for exposing a human | ||
42 | * readable state via sysfs. Always keep in sync with enum mic_shutdown_status | ||
43 | */ | ||
44 | static const char * const mic_shutdown_status_string[] = { | ||
45 | [MIC_NOP] = "nop", | ||
46 | [MIC_CRASHED] = "crashed", | ||
47 | [MIC_HALTED] = "halted", | ||
48 | [MIC_POWER_OFF] = "poweroff", | ||
49 | [MIC_RESTART] = "restart", | ||
50 | }; | ||
51 | |||
52 | void mic_set_shutdown_status(struct mic_device *mdev, u8 shutdown_status) | ||
53 | { | ||
54 | dev_dbg(mdev->sdev->parent, "Shutdown Status %s -> %s\n", | ||
55 | mic_shutdown_status_string[mdev->shutdown_status], | ||
56 | mic_shutdown_status_string[shutdown_status]); | ||
57 | mdev->shutdown_status = shutdown_status; | ||
58 | } | ||
59 | |||
60 | void mic_set_state(struct mic_device *mdev, u8 state) | ||
61 | { | ||
62 | dev_dbg(mdev->sdev->parent, "State %s -> %s\n", | ||
63 | mic_state_string[mdev->state], | ||
64 | mic_state_string[state]); | ||
65 | mdev->state = state; | ||
66 | sysfs_notify_dirent(mdev->state_sysfs); | ||
67 | } | ||
68 | |||
69 | static ssize_t | ||
70 | family_show(struct device *dev, struct device_attribute *attr, char *buf) | ||
71 | { | ||
72 | static const char x100[] = "x100"; | ||
73 | static const char unknown[] = "Unknown"; | ||
74 | const char *card = NULL; | ||
75 | struct mic_device *mdev = dev_get_drvdata(dev->parent); | ||
76 | |||
77 | if (!mdev) | ||
78 | return -EINVAL; | ||
79 | |||
80 | switch (mdev->family) { | ||
81 | case MIC_FAMILY_X100: | ||
82 | card = x100; | ||
83 | break; | ||
84 | default: | ||
85 | card = unknown; | ||
86 | break; | ||
87 | } | ||
88 | return scnprintf(buf, PAGE_SIZE, "%s\n", card); | ||
89 | } | ||
90 | static DEVICE_ATTR_RO(family); | ||
91 | |||
92 | static ssize_t | ||
93 | stepping_show(struct device *dev, struct device_attribute *attr, char *buf) | ||
94 | { | ||
95 | struct mic_device *mdev = dev_get_drvdata(dev->parent); | ||
96 | char *string = "??"; | ||
97 | |||
98 | if (!mdev) | ||
99 | return -EINVAL; | ||
100 | |||
101 | switch (mdev->stepping) { | ||
102 | case MIC_A0_STEP: | ||
103 | string = "A0"; | ||
104 | break; | ||
105 | case MIC_B0_STEP: | ||
106 | string = "B0"; | ||
107 | break; | ||
108 | case MIC_B1_STEP: | ||
109 | string = "B1"; | ||
110 | break; | ||
111 | case MIC_C0_STEP: | ||
112 | string = "C0"; | ||
113 | break; | ||
114 | default: | ||
115 | break; | ||
116 | } | ||
117 | return scnprintf(buf, PAGE_SIZE, "%s\n", string); | ||
118 | } | ||
119 | static DEVICE_ATTR_RO(stepping); | ||
120 | |||
121 | static ssize_t | ||
122 | state_show(struct device *dev, struct device_attribute *attr, char *buf) | ||
123 | { | ||
124 | struct mic_device *mdev = dev_get_drvdata(dev->parent); | ||
125 | |||
126 | if (!mdev || mdev->state >= MIC_LAST) | ||
127 | return -EINVAL; | ||
128 | |||
129 | return scnprintf(buf, PAGE_SIZE, "%s\n", | ||
130 | mic_state_string[mdev->state]); | ||
131 | } | ||
132 | |||
133 | static ssize_t | ||
134 | state_store(struct device *dev, struct device_attribute *attr, | ||
135 | const char *buf, size_t count) | ||
136 | { | ||
137 | int rc = 0; | ||
138 | struct mic_device *mdev = dev_get_drvdata(dev->parent); | ||
139 | if (!mdev) | ||
140 | return -EINVAL; | ||
141 | if (sysfs_streq(buf, "boot")) { | ||
142 | rc = mic_start(mdev, buf); | ||
143 | if (rc) { | ||
144 | dev_err(mdev->sdev->parent, | ||
145 | "mic_boot failed rc %d\n", rc); | ||
146 | count = rc; | ||
147 | } | ||
148 | goto done; | ||
149 | } | ||
150 | |||
151 | if (sysfs_streq(buf, "reset")) { | ||
152 | schedule_work(&mdev->reset_trigger_work); | ||
153 | goto done; | ||
154 | } | ||
155 | |||
156 | if (sysfs_streq(buf, "shutdown")) { | ||
157 | mic_shutdown(mdev); | ||
158 | goto done; | ||
159 | } | ||
160 | |||
161 | if (sysfs_streq(buf, "suspend")) { | ||
162 | mic_suspend(mdev); | ||
163 | goto done; | ||
164 | } | ||
165 | |||
166 | count = -EINVAL; | ||
167 | done: | ||
168 | return count; | ||
169 | } | ||
170 | static DEVICE_ATTR_RW(state); | ||
171 | |||
172 | static ssize_t shutdown_status_show(struct device *dev, | ||
173 | struct device_attribute *attr, char *buf) | ||
174 | { | ||
175 | struct mic_device *mdev = dev_get_drvdata(dev->parent); | ||
176 | |||
177 | if (!mdev || mdev->shutdown_status >= MIC_STATUS_LAST) | ||
178 | return -EINVAL; | ||
179 | |||
180 | return scnprintf(buf, PAGE_SIZE, "%s\n", | ||
181 | mic_shutdown_status_string[mdev->shutdown_status]); | ||
182 | } | ||
183 | static DEVICE_ATTR_RO(shutdown_status); | ||
184 | |||
185 | static ssize_t | ||
186 | cmdline_show(struct device *dev, struct device_attribute *attr, char *buf) | ||
187 | { | ||
188 | struct mic_device *mdev = dev_get_drvdata(dev->parent); | ||
189 | char *cmdline; | ||
190 | |||
191 | if (!mdev) | ||
192 | return -EINVAL; | ||
193 | |||
194 | cmdline = mdev->cmdline; | ||
195 | |||
196 | if (cmdline) | ||
197 | return scnprintf(buf, PAGE_SIZE, "%s\n", cmdline); | ||
198 | return 0; | ||
199 | } | ||
200 | |||
201 | static ssize_t | ||
202 | cmdline_store(struct device *dev, struct device_attribute *attr, | ||
203 | const char *buf, size_t count) | ||
204 | { | ||
205 | struct mic_device *mdev = dev_get_drvdata(dev->parent); | ||
206 | |||
207 | if (!mdev) | ||
208 | return -EINVAL; | ||
209 | |||
210 | mutex_lock(&mdev->mic_mutex); | ||
211 | kfree(mdev->cmdline); | ||
212 | |||
213 | mdev->cmdline = kmalloc(count + 1, GFP_KERNEL); | ||
214 | if (!mdev->cmdline) { | ||
215 | count = -ENOMEM; | ||
216 | goto unlock; | ||
217 | } | ||
218 | |||
219 | strncpy(mdev->cmdline, buf, count); | ||
220 | |||
221 | if (mdev->cmdline[count - 1] == '\n') | ||
222 | mdev->cmdline[count - 1] = '\0'; | ||
223 | else | ||
224 | mdev->cmdline[count] = '\0'; | ||
225 | unlock: | ||
226 | mutex_unlock(&mdev->mic_mutex); | ||
227 | return count; | ||
228 | } | ||
229 | static DEVICE_ATTR_RW(cmdline); | ||
230 | |||
231 | static ssize_t | ||
232 | firmware_show(struct device *dev, struct device_attribute *attr, char *buf) | ||
233 | { | ||
234 | struct mic_device *mdev = dev_get_drvdata(dev->parent); | ||
235 | char *firmware; | ||
236 | |||
237 | if (!mdev) | ||
238 | return -EINVAL; | ||
239 | |||
240 | firmware = mdev->firmware; | ||
241 | |||
242 | if (firmware) | ||
243 | return scnprintf(buf, PAGE_SIZE, "%s\n", firmware); | ||
244 | return 0; | ||
245 | } | ||
246 | |||
247 | static ssize_t | ||
248 | firmware_store(struct device *dev, struct device_attribute *attr, | ||
249 | const char *buf, size_t count) | ||
250 | { | ||
251 | struct mic_device *mdev = dev_get_drvdata(dev->parent); | ||
252 | |||
253 | if (!mdev) | ||
254 | return -EINVAL; | ||
255 | |||
256 | mutex_lock(&mdev->mic_mutex); | ||
257 | kfree(mdev->firmware); | ||
258 | |||
259 | mdev->firmware = kmalloc(count + 1, GFP_KERNEL); | ||
260 | if (!mdev->firmware) { | ||
261 | count = -ENOMEM; | ||
262 | goto unlock; | ||
263 | } | ||
264 | strncpy(mdev->firmware, buf, count); | ||
265 | |||
266 | if (mdev->firmware[count - 1] == '\n') | ||
267 | mdev->firmware[count - 1] = '\0'; | ||
268 | else | ||
269 | mdev->firmware[count] = '\0'; | ||
270 | unlock: | ||
271 | mutex_unlock(&mdev->mic_mutex); | ||
272 | return count; | ||
273 | } | ||
274 | static DEVICE_ATTR_RW(firmware); | ||
275 | |||
276 | static ssize_t | ||
277 | ramdisk_show(struct device *dev, struct device_attribute *attr, char *buf) | ||
278 | { | ||
279 | struct mic_device *mdev = dev_get_drvdata(dev->parent); | ||
280 | char *ramdisk; | ||
281 | |||
282 | if (!mdev) | ||
283 | return -EINVAL; | ||
284 | |||
285 | ramdisk = mdev->ramdisk; | ||
286 | |||
287 | if (ramdisk) | ||
288 | return scnprintf(buf, PAGE_SIZE, "%s\n", ramdisk); | ||
289 | return 0; | ||
290 | } | ||
291 | |||
292 | static ssize_t | ||
293 | ramdisk_store(struct device *dev, struct device_attribute *attr, | ||
294 | const char *buf, size_t count) | ||
295 | { | ||
296 | struct mic_device *mdev = dev_get_drvdata(dev->parent); | ||
297 | |||
298 | if (!mdev) | ||
299 | return -EINVAL; | ||
300 | |||
301 | mutex_lock(&mdev->mic_mutex); | ||
302 | kfree(mdev->ramdisk); | ||
303 | |||
304 | mdev->ramdisk = kmalloc(count + 1, GFP_KERNEL); | ||
305 | if (!mdev->ramdisk) { | ||
306 | count = -ENOMEM; | ||
307 | goto unlock; | ||
308 | } | ||
309 | |||
310 | strncpy(mdev->ramdisk, buf, count); | ||
311 | |||
312 | if (mdev->ramdisk[count - 1] == '\n') | ||
313 | mdev->ramdisk[count - 1] = '\0'; | ||
314 | else | ||
315 | mdev->ramdisk[count] = '\0'; | ||
316 | unlock: | ||
317 | mutex_unlock(&mdev->mic_mutex); | ||
318 | return count; | ||
319 | } | ||
320 | static DEVICE_ATTR_RW(ramdisk); | ||
321 | |||
322 | static ssize_t | ||
323 | bootmode_show(struct device *dev, struct device_attribute *attr, char *buf) | ||
324 | { | ||
325 | struct mic_device *mdev = dev_get_drvdata(dev->parent); | ||
326 | char *bootmode; | ||
327 | |||
328 | if (!mdev) | ||
329 | return -EINVAL; | ||
330 | |||
331 | bootmode = mdev->bootmode; | ||
332 | |||
333 | if (bootmode) | ||
334 | return scnprintf(buf, PAGE_SIZE, "%s\n", bootmode); | ||
335 | return 0; | ||
336 | } | ||
337 | |||
338 | static ssize_t | ||
339 | bootmode_store(struct device *dev, struct device_attribute *attr, | ||
340 | const char *buf, size_t count) | ||
341 | { | ||
342 | struct mic_device *mdev = dev_get_drvdata(dev->parent); | ||
343 | |||
344 | if (!mdev) | ||
345 | return -EINVAL; | ||
346 | |||
347 | if (!sysfs_streq(buf, "linux") && !sysfs_streq(buf, "elf")) | ||
348 | return -EINVAL; | ||
349 | |||
350 | mutex_lock(&mdev->mic_mutex); | ||
351 | kfree(mdev->bootmode); | ||
352 | |||
353 | mdev->bootmode = kmalloc(count + 1, GFP_KERNEL); | ||
354 | if (!mdev->bootmode) { | ||
355 | count = -ENOMEM; | ||
356 | goto unlock; | ||
357 | } | ||
358 | |||
359 | strncpy(mdev->bootmode, buf, count); | ||
360 | |||
361 | if (mdev->bootmode[count - 1] == '\n') | ||
362 | mdev->bootmode[count - 1] = '\0'; | ||
363 | else | ||
364 | mdev->bootmode[count] = '\0'; | ||
365 | unlock: | ||
366 | mutex_unlock(&mdev->mic_mutex); | ||
367 | return count; | ||
368 | } | ||
369 | static DEVICE_ATTR_RW(bootmode); | ||
370 | |||
371 | static ssize_t | ||
372 | log_buf_addr_show(struct device *dev, struct device_attribute *attr, | ||
373 | char *buf) | ||
374 | { | ||
375 | struct mic_device *mdev = dev_get_drvdata(dev->parent); | ||
376 | |||
377 | if (!mdev) | ||
378 | return -EINVAL; | ||
379 | |||
380 | return scnprintf(buf, PAGE_SIZE, "%p\n", mdev->log_buf_addr); | ||
381 | } | ||
382 | |||
383 | static ssize_t | ||
384 | log_buf_addr_store(struct device *dev, struct device_attribute *attr, | ||
385 | const char *buf, size_t count) | ||
386 | { | ||
387 | struct mic_device *mdev = dev_get_drvdata(dev->parent); | ||
388 | int ret; | ||
389 | unsigned long addr; | ||
390 | |||
391 | if (!mdev) | ||
392 | return -EINVAL; | ||
393 | |||
394 | ret = kstrtoul(buf, 16, &addr); | ||
395 | if (ret) | ||
396 | goto exit; | ||
397 | |||
398 | mdev->log_buf_addr = (void *)addr; | ||
399 | ret = count; | ||
400 | exit: | ||
401 | return ret; | ||
402 | } | ||
403 | static DEVICE_ATTR_RW(log_buf_addr); | ||
404 | |||
405 | static ssize_t | ||
406 | log_buf_len_show(struct device *dev, struct device_attribute *attr, | ||
407 | char *buf) | ||
408 | { | ||
409 | struct mic_device *mdev = dev_get_drvdata(dev->parent); | ||
410 | |||
411 | if (!mdev) | ||
412 | return -EINVAL; | ||
413 | |||
414 | return scnprintf(buf, PAGE_SIZE, "%p\n", mdev->log_buf_len); | ||
415 | } | ||
416 | |||
417 | static ssize_t | ||
418 | log_buf_len_store(struct device *dev, struct device_attribute *attr, | ||
419 | const char *buf, size_t count) | ||
420 | { | ||
421 | struct mic_device *mdev = dev_get_drvdata(dev->parent); | ||
422 | int ret; | ||
423 | unsigned long addr; | ||
424 | |||
425 | if (!mdev) | ||
426 | return -EINVAL; | ||
427 | |||
428 | ret = kstrtoul(buf, 16, &addr); | ||
429 | if (ret) | ||
430 | goto exit; | ||
431 | |||
432 | mdev->log_buf_len = (int *)addr; | ||
433 | ret = count; | ||
434 | exit: | ||
435 | return ret; | ||
436 | } | ||
437 | static DEVICE_ATTR_RW(log_buf_len); | ||
438 | |||
439 | static struct attribute *mic_default_attrs[] = { | ||
440 | &dev_attr_family.attr, | ||
441 | &dev_attr_stepping.attr, | ||
442 | &dev_attr_state.attr, | ||
443 | &dev_attr_shutdown_status.attr, | ||
444 | &dev_attr_cmdline.attr, | ||
445 | &dev_attr_firmware.attr, | ||
446 | &dev_attr_ramdisk.attr, | ||
447 | &dev_attr_bootmode.attr, | ||
448 | &dev_attr_log_buf_addr.attr, | ||
449 | &dev_attr_log_buf_len.attr, | ||
450 | |||
451 | NULL | ||
452 | }; | ||
453 | |||
454 | ATTRIBUTE_GROUPS(mic_default); | ||
455 | |||
456 | void mic_sysfs_init(struct mic_device *mdev) | ||
457 | { | ||
458 | mdev->attr_group = mic_default_groups; | ||
459 | } | ||
diff --git a/drivers/misc/mic/host/mic_virtio.c b/drivers/misc/mic/host/mic_virtio.c new file mode 100644 index 000000000000..5b8494bd1e00 --- /dev/null +++ b/drivers/misc/mic/host/mic_virtio.c | |||
@@ -0,0 +1,700 @@ | |||
1 | /* | ||
2 | * Intel MIC Platform Software Stack (MPSS) | ||
3 | * | ||
4 | * Copyright(c) 2013 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 Host driver. | ||
19 | * | ||
20 | */ | ||
21 | #include <linux/pci.h> | ||
22 | #include <linux/sched.h> | ||
23 | #include <linux/uaccess.h> | ||
24 | |||
25 | #include <linux/mic_common.h> | ||
26 | #include "../common/mic_dev.h" | ||
27 | #include "mic_device.h" | ||
28 | #include "mic_smpt.h" | ||
29 | #include "mic_virtio.h" | ||
30 | |||
31 | /* | ||
32 | * Initiates the copies across the PCIe bus from card memory to | ||
33 | * a user space buffer. | ||
34 | */ | ||
35 | static int mic_virtio_copy_to_user(struct mic_vdev *mvdev, | ||
36 | void __user *ubuf, size_t len, u64 addr) | ||
37 | { | ||
38 | int err; | ||
39 | void __iomem *dbuf = mvdev->mdev->aper.va + addr; | ||
40 | /* | ||
41 | * We are copying from IO below an should ideally use something | ||
42 | * like copy_to_user_fromio(..) if it existed. | ||
43 | */ | ||
44 | if (copy_to_user(ubuf, dbuf, len)) { | ||
45 | err = -EFAULT; | ||
46 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | ||
47 | __func__, __LINE__, err); | ||
48 | goto err; | ||
49 | } | ||
50 | mvdev->in_bytes += len; | ||
51 | err = 0; | ||
52 | err: | ||
53 | return err; | ||
54 | } | ||
55 | |||
56 | /* | ||
57 | * Initiates copies across the PCIe bus from a user space | ||
58 | * buffer to card memory. | ||
59 | */ | ||
60 | static int mic_virtio_copy_from_user(struct mic_vdev *mvdev, | ||
61 | void __user *ubuf, size_t len, u64 addr) | ||
62 | { | ||
63 | int err; | ||
64 | void __iomem *dbuf = mvdev->mdev->aper.va + addr; | ||
65 | /* | ||
66 | * We are copying to IO below and should ideally use something | ||
67 | * like copy_from_user_toio(..) if it existed. | ||
68 | */ | ||
69 | if (copy_from_user(dbuf, ubuf, len)) { | ||
70 | err = -EFAULT; | ||
71 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | ||
72 | __func__, __LINE__, err); | ||
73 | goto err; | ||
74 | } | ||
75 | mvdev->out_bytes += len; | ||
76 | err = 0; | ||
77 | err: | ||
78 | return err; | ||
79 | } | ||
80 | |||
81 | #define MIC_VRINGH_READ true | ||
82 | |||
83 | /* The function to call to notify the card about added buffers */ | ||
84 | static void mic_notify(struct vringh *vrh) | ||
85 | { | ||
86 | struct mic_vringh *mvrh = container_of(vrh, struct mic_vringh, vrh); | ||
87 | struct mic_vdev *mvdev = mvrh->mvdev; | ||
88 | s8 db = mvdev->dc->h2c_vdev_db; | ||
89 | |||
90 | if (db != -1) | ||
91 | mvdev->mdev->ops->send_intr(mvdev->mdev, db); | ||
92 | } | ||
93 | |||
94 | /* Determine the total number of bytes consumed in a VRINGH KIOV */ | ||
95 | static inline u32 mic_vringh_iov_consumed(struct vringh_kiov *iov) | ||
96 | { | ||
97 | int i; | ||
98 | u32 total = iov->consumed; | ||
99 | |||
100 | for (i = 0; i < iov->i; i++) | ||
101 | total += iov->iov[i].iov_len; | ||
102 | return total; | ||
103 | } | ||
104 | |||
105 | /* | ||
106 | * Traverse the VRINGH KIOV and issue the APIs to trigger the copies. | ||
107 | * This API is heavily based on the vringh_iov_xfer(..) implementation | ||
108 | * in vringh.c. The reason we cannot reuse vringh_iov_pull_kern(..) | ||
109 | * and vringh_iov_push_kern(..) directly is because there is no | ||
110 | * way to override the VRINGH xfer(..) routines as of v3.10. | ||
111 | */ | ||
112 | 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) | ||
114 | { | ||
115 | int ret = 0; | ||
116 | size_t partlen, tot_len = 0; | ||
117 | |||
118 | while (len && iov->i < iov->used) { | ||
119 | partlen = min(iov->iov[iov->i].iov_len, len); | ||
120 | if (read) | ||
121 | ret = mic_virtio_copy_to_user(mvdev, | ||
122 | ubuf, partlen, | ||
123 | (u64)iov->iov[iov->i].iov_base); | ||
124 | else | ||
125 | ret = mic_virtio_copy_from_user(mvdev, | ||
126 | ubuf, partlen, | ||
127 | (u64)iov->iov[iov->i].iov_base); | ||
128 | if (ret) { | ||
129 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | ||
130 | __func__, __LINE__, ret); | ||
131 | break; | ||
132 | } | ||
133 | len -= partlen; | ||
134 | ubuf += partlen; | ||
135 | tot_len += partlen; | ||
136 | iov->consumed += partlen; | ||
137 | iov->iov[iov->i].iov_len -= partlen; | ||
138 | iov->iov[iov->i].iov_base += partlen; | ||
139 | if (!iov->iov[iov->i].iov_len) { | ||
140 | /* Fix up old iov element then increment. */ | ||
141 | iov->iov[iov->i].iov_len = iov->consumed; | ||
142 | iov->iov[iov->i].iov_base -= iov->consumed; | ||
143 | |||
144 | iov->consumed = 0; | ||
145 | iov->i++; | ||
146 | } | ||
147 | } | ||
148 | *out_len = tot_len; | ||
149 | return ret; | ||
150 | } | ||
151 | |||
152 | /* | ||
153 | * Use the standard VRINGH infrastructure in the kernel to fetch new | ||
154 | * descriptors, initiate the copies and update the used ring. | ||
155 | */ | ||
156 | static int _mic_virtio_copy(struct mic_vdev *mvdev, | ||
157 | struct mic_copy_desc *copy) | ||
158 | { | ||
159 | int ret = 0, iovcnt = copy->iovcnt; | ||
160 | struct iovec iov; | ||
161 | struct iovec __user *u_iov = copy->iov; | ||
162 | void __user *ubuf = NULL; | ||
163 | struct mic_vringh *mvr = &mvdev->mvr[copy->vr_idx]; | ||
164 | struct vringh_kiov *riov = &mvr->riov; | ||
165 | struct vringh_kiov *wiov = &mvr->wiov; | ||
166 | struct vringh *vrh = &mvr->vrh; | ||
167 | u16 *head = &mvr->head; | ||
168 | struct mic_vring *vr = &mvr->vring; | ||
169 | size_t len = 0, out_len; | ||
170 | |||
171 | copy->out_len = 0; | ||
172 | /* Fetch a new IOVEC if all previous elements have been processed */ | ||
173 | if (riov->i == riov->used && wiov->i == wiov->used) { | ||
174 | ret = vringh_getdesc_kern(vrh, riov, wiov, | ||
175 | head, GFP_KERNEL); | ||
176 | /* Check if there are available descriptors */ | ||
177 | if (ret <= 0) | ||
178 | return ret; | ||
179 | } | ||
180 | while (iovcnt) { | ||
181 | if (!len) { | ||
182 | /* Copy over a new iovec from user space. */ | ||
183 | ret = copy_from_user(&iov, u_iov, sizeof(*u_iov)); | ||
184 | if (ret) { | ||
185 | ret = -EINVAL; | ||
186 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | ||
187 | __func__, __LINE__, ret); | ||
188 | break; | ||
189 | } | ||
190 | len = iov.iov_len; | ||
191 | ubuf = iov.iov_base; | ||
192 | } | ||
193 | /* Issue all the read descriptors first */ | ||
194 | ret = mic_vringh_copy(mvdev, riov, ubuf, len, | ||
195 | MIC_VRINGH_READ, &out_len); | ||
196 | if (ret) { | ||
197 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | ||
198 | __func__, __LINE__, ret); | ||
199 | break; | ||
200 | } | ||
201 | len -= out_len; | ||
202 | ubuf += out_len; | ||
203 | copy->out_len += out_len; | ||
204 | /* Issue the write descriptors next */ | ||
205 | ret = mic_vringh_copy(mvdev, wiov, ubuf, len, | ||
206 | !MIC_VRINGH_READ, &out_len); | ||
207 | if (ret) { | ||
208 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | ||
209 | __func__, __LINE__, ret); | ||
210 | break; | ||
211 | } | ||
212 | len -= out_len; | ||
213 | ubuf += out_len; | ||
214 | copy->out_len += out_len; | ||
215 | if (!len) { | ||
216 | /* One user space iovec is now completed */ | ||
217 | iovcnt--; | ||
218 | u_iov++; | ||
219 | } | ||
220 | /* Exit loop if all elements in KIOVs have been processed. */ | ||
221 | if (riov->i == riov->used && wiov->i == wiov->used) | ||
222 | break; | ||
223 | } | ||
224 | /* | ||
225 | * Update the used ring if a descriptor was available and some data was | ||
226 | * copied in/out and the user asked for a used ring update. | ||
227 | */ | ||
228 | if (*head != USHRT_MAX && copy->out_len && copy->update_used) { | ||
229 | u32 total = 0; | ||
230 | |||
231 | /* Determine the total data consumed */ | ||
232 | total += mic_vringh_iov_consumed(riov); | ||
233 | total += mic_vringh_iov_consumed(wiov); | ||
234 | vringh_complete_kern(vrh, *head, total); | ||
235 | *head = USHRT_MAX; | ||
236 | if (vringh_need_notify_kern(vrh) > 0) | ||
237 | vringh_notify(vrh); | ||
238 | vringh_kiov_cleanup(riov); | ||
239 | vringh_kiov_cleanup(wiov); | ||
240 | /* Update avail idx for user space */ | ||
241 | vr->info->avail_idx = vrh->last_avail_idx; | ||
242 | } | ||
243 | return ret; | ||
244 | } | ||
245 | |||
246 | static inline int mic_verify_copy_args(struct mic_vdev *mvdev, | ||
247 | struct mic_copy_desc *copy) | ||
248 | { | ||
249 | if (copy->vr_idx >= mvdev->dd->num_vq) { | ||
250 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | ||
251 | __func__, __LINE__, -EINVAL); | ||
252 | return -EINVAL; | ||
253 | } | ||
254 | return 0; | ||
255 | } | ||
256 | |||
257 | /* Copy a specified number of virtio descriptors in a chain */ | ||
258 | int mic_virtio_copy_desc(struct mic_vdev *mvdev, | ||
259 | struct mic_copy_desc *copy) | ||
260 | { | ||
261 | int err; | ||
262 | struct mic_vringh *mvr = &mvdev->mvr[copy->vr_idx]; | ||
263 | |||
264 | err = mic_verify_copy_args(mvdev, copy); | ||
265 | if (err) | ||
266 | return err; | ||
267 | |||
268 | mutex_lock(&mvr->vr_mutex); | ||
269 | if (!mic_vdevup(mvdev)) { | ||
270 | err = -ENODEV; | ||
271 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | ||
272 | __func__, __LINE__, err); | ||
273 | goto err; | ||
274 | } | ||
275 | err = _mic_virtio_copy(mvdev, copy); | ||
276 | if (err) { | ||
277 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | ||
278 | __func__, __LINE__, err); | ||
279 | } | ||
280 | err: | ||
281 | mutex_unlock(&mvr->vr_mutex); | ||
282 | return err; | ||
283 | } | ||
284 | |||
285 | static void mic_virtio_init_post(struct mic_vdev *mvdev) | ||
286 | { | ||
287 | struct mic_vqconfig *vqconfig = mic_vq_config(mvdev->dd); | ||
288 | int i; | ||
289 | |||
290 | for (i = 0; i < mvdev->dd->num_vq; i++) { | ||
291 | if (!le64_to_cpu(vqconfig[i].used_address)) { | ||
292 | dev_warn(mic_dev(mvdev), "used_address zero??\n"); | ||
293 | continue; | ||
294 | } | ||
295 | mvdev->mvr[i].vrh.vring.used = | ||
296 | mvdev->mdev->aper.va + | ||
297 | le64_to_cpu(vqconfig[i].used_address); | ||
298 | } | ||
299 | |||
300 | mvdev->dc->used_address_updated = 0; | ||
301 | |||
302 | dev_dbg(mic_dev(mvdev), "%s: device type %d LINKUP\n", | ||
303 | __func__, mvdev->virtio_id); | ||
304 | } | ||
305 | |||
306 | static inline void mic_virtio_device_reset(struct mic_vdev *mvdev) | ||
307 | { | ||
308 | int i; | ||
309 | |||
310 | dev_dbg(mic_dev(mvdev), "%s: status %d device type %d RESET\n", | ||
311 | __func__, mvdev->dd->status, mvdev->virtio_id); | ||
312 | |||
313 | for (i = 0; i < mvdev->dd->num_vq; i++) | ||
314 | /* | ||
315 | * Avoid lockdep false positive. The + 1 is for the mic | ||
316 | * mutex which is held in the reset devices code path. | ||
317 | */ | ||
318 | mutex_lock_nested(&mvdev->mvr[i].vr_mutex, i + 1); | ||
319 | |||
320 | /* 0 status means "reset" */ | ||
321 | mvdev->dd->status = 0; | ||
322 | mvdev->dc->vdev_reset = 0; | ||
323 | mvdev->dc->host_ack = 1; | ||
324 | |||
325 | for (i = 0; i < mvdev->dd->num_vq; i++) { | ||
326 | struct vringh *vrh = &mvdev->mvr[i].vrh; | ||
327 | mvdev->mvr[i].vring.info->avail_idx = 0; | ||
328 | vrh->completed = 0; | ||
329 | vrh->last_avail_idx = 0; | ||
330 | vrh->last_used_idx = 0; | ||
331 | } | ||
332 | |||
333 | for (i = 0; i < mvdev->dd->num_vq; i++) | ||
334 | mutex_unlock(&mvdev->mvr[i].vr_mutex); | ||
335 | } | ||
336 | |||
337 | void mic_virtio_reset_devices(struct mic_device *mdev) | ||
338 | { | ||
339 | struct list_head *pos, *tmp; | ||
340 | struct mic_vdev *mvdev; | ||
341 | |||
342 | dev_dbg(mdev->sdev->parent, "%s\n", __func__); | ||
343 | |||
344 | list_for_each_safe(pos, tmp, &mdev->vdev_list) { | ||
345 | mvdev = list_entry(pos, struct mic_vdev, list); | ||
346 | mic_virtio_device_reset(mvdev); | ||
347 | mvdev->poll_wake = 1; | ||
348 | wake_up(&mvdev->waitq); | ||
349 | } | ||
350 | } | ||
351 | |||
352 | void mic_bh_handler(struct work_struct *work) | ||
353 | { | ||
354 | struct mic_vdev *mvdev = container_of(work, struct mic_vdev, | ||
355 | virtio_bh_work); | ||
356 | |||
357 | if (mvdev->dc->used_address_updated) | ||
358 | mic_virtio_init_post(mvdev); | ||
359 | |||
360 | if (mvdev->dc->vdev_reset) | ||
361 | mic_virtio_device_reset(mvdev); | ||
362 | |||
363 | mvdev->poll_wake = 1; | ||
364 | wake_up(&mvdev->waitq); | ||
365 | } | ||
366 | |||
367 | static irqreturn_t mic_virtio_intr_handler(int irq, void *data) | ||
368 | { | ||
369 | struct mic_vdev *mvdev = data; | ||
370 | struct mic_device *mdev = mvdev->mdev; | ||
371 | |||
372 | mdev->ops->ack_interrupt(mdev); | ||
373 | schedule_work(&mvdev->virtio_bh_work); | ||
374 | return IRQ_HANDLED; | ||
375 | } | ||
376 | |||
377 | int mic_virtio_config_change(struct mic_vdev *mvdev, | ||
378 | void __user *argp) | ||
379 | { | ||
380 | DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wake); | ||
381 | int ret = 0, retry = 100, i; | ||
382 | struct mic_bootparam *bootparam = mvdev->mdev->dp; | ||
383 | s8 db = bootparam->h2c_config_db; | ||
384 | |||
385 | mutex_lock(&mvdev->mdev->mic_mutex); | ||
386 | for (i = 0; i < mvdev->dd->num_vq; i++) | ||
387 | mutex_lock_nested(&mvdev->mvr[i].vr_mutex, i + 1); | ||
388 | |||
389 | if (db == -1 || mvdev->dd->type == -1) { | ||
390 | ret = -EIO; | ||
391 | goto exit; | ||
392 | } | ||
393 | |||
394 | if (copy_from_user(mic_vq_configspace(mvdev->dd), | ||
395 | argp, mvdev->dd->config_len)) { | ||
396 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | ||
397 | __func__, __LINE__, -EFAULT); | ||
398 | ret = -EFAULT; | ||
399 | goto exit; | ||
400 | } | ||
401 | mvdev->dc->config_change = MIC_VIRTIO_PARAM_CONFIG_CHANGED; | ||
402 | mvdev->mdev->ops->send_intr(mvdev->mdev, db); | ||
403 | |||
404 | for (i = retry; i--;) { | ||
405 | ret = wait_event_timeout(wake, | ||
406 | mvdev->dc->guest_ack, msecs_to_jiffies(100)); | ||
407 | if (ret) | ||
408 | break; | ||
409 | } | ||
410 | |||
411 | dev_dbg(mic_dev(mvdev), | ||
412 | "%s %d retry: %d\n", __func__, __LINE__, retry); | ||
413 | mvdev->dc->config_change = 0; | ||
414 | mvdev->dc->guest_ack = 0; | ||
415 | exit: | ||
416 | for (i = 0; i < mvdev->dd->num_vq; i++) | ||
417 | mutex_unlock(&mvdev->mvr[i].vr_mutex); | ||
418 | mutex_unlock(&mvdev->mdev->mic_mutex); | ||
419 | return ret; | ||
420 | } | ||
421 | |||
422 | static int mic_copy_dp_entry(struct mic_vdev *mvdev, | ||
423 | void __user *argp, | ||
424 | __u8 *type, | ||
425 | struct mic_device_desc **devpage) | ||
426 | { | ||
427 | struct mic_device *mdev = mvdev->mdev; | ||
428 | struct mic_device_desc dd, *dd_config, *devp; | ||
429 | struct mic_vqconfig *vqconfig; | ||
430 | int ret = 0, i; | ||
431 | bool slot_found = false; | ||
432 | |||
433 | if (copy_from_user(&dd, argp, sizeof(dd))) { | ||
434 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | ||
435 | __func__, __LINE__, -EFAULT); | ||
436 | return -EFAULT; | ||
437 | } | ||
438 | |||
439 | if (mic_aligned_desc_size(&dd) > MIC_MAX_DESC_BLK_SIZE || | ||
440 | dd.num_vq > MIC_MAX_VRINGS) { | ||
441 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | ||
442 | __func__, __LINE__, -EINVAL); | ||
443 | return -EINVAL; | ||
444 | } | ||
445 | |||
446 | dd_config = kmalloc(mic_desc_size(&dd), GFP_KERNEL); | ||
447 | if (dd_config == NULL) { | ||
448 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | ||
449 | __func__, __LINE__, -ENOMEM); | ||
450 | return -ENOMEM; | ||
451 | } | ||
452 | if (copy_from_user(dd_config, argp, mic_desc_size(&dd))) { | ||
453 | ret = -EFAULT; | ||
454 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | ||
455 | __func__, __LINE__, ret); | ||
456 | goto exit; | ||
457 | } | ||
458 | |||
459 | vqconfig = mic_vq_config(dd_config); | ||
460 | for (i = 0; i < dd.num_vq; i++) { | ||
461 | if (le16_to_cpu(vqconfig[i].num) > MIC_MAX_VRING_ENTRIES) { | ||
462 | ret = -EINVAL; | ||
463 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | ||
464 | __func__, __LINE__, ret); | ||
465 | goto exit; | ||
466 | } | ||
467 | } | ||
468 | |||
469 | /* Find the first free device page entry */ | ||
470 | for (i = mic_aligned_size(struct mic_bootparam); | ||
471 | i < MIC_DP_SIZE - mic_total_desc_size(dd_config); | ||
472 | i += mic_total_desc_size(devp)) { | ||
473 | devp = mdev->dp + i; | ||
474 | if (devp->type == 0 || devp->type == -1) { | ||
475 | slot_found = true; | ||
476 | break; | ||
477 | } | ||
478 | } | ||
479 | if (!slot_found) { | ||
480 | ret = -EINVAL; | ||
481 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | ||
482 | __func__, __LINE__, ret); | ||
483 | goto exit; | ||
484 | } | ||
485 | /* | ||
486 | * Save off the type before doing the memcpy. Type will be set in the | ||
487 | * end after completing all initialization for the new device. | ||
488 | */ | ||
489 | *type = dd_config->type; | ||
490 | dd_config->type = 0; | ||
491 | memcpy(devp, dd_config, mic_desc_size(dd_config)); | ||
492 | |||
493 | *devpage = devp; | ||
494 | exit: | ||
495 | kfree(dd_config); | ||
496 | return ret; | ||
497 | } | ||
498 | |||
499 | static void mic_init_device_ctrl(struct mic_vdev *mvdev, | ||
500 | struct mic_device_desc *devpage) | ||
501 | { | ||
502 | struct mic_device_ctrl *dc; | ||
503 | |||
504 | dc = (void *)devpage + mic_aligned_desc_size(devpage); | ||
505 | |||
506 | dc->config_change = 0; | ||
507 | dc->guest_ack = 0; | ||
508 | dc->vdev_reset = 0; | ||
509 | dc->host_ack = 0; | ||
510 | dc->used_address_updated = 0; | ||
511 | dc->c2h_vdev_db = -1; | ||
512 | dc->h2c_vdev_db = -1; | ||
513 | mvdev->dc = dc; | ||
514 | } | ||
515 | |||
516 | int mic_virtio_add_device(struct mic_vdev *mvdev, | ||
517 | void __user *argp) | ||
518 | { | ||
519 | struct mic_device *mdev = mvdev->mdev; | ||
520 | struct mic_device_desc *dd = NULL; | ||
521 | struct mic_vqconfig *vqconfig; | ||
522 | int vr_size, i, j, ret; | ||
523 | u8 type = 0; | ||
524 | s8 db; | ||
525 | char irqname[10]; | ||
526 | struct mic_bootparam *bootparam = mdev->dp; | ||
527 | u16 num; | ||
528 | |||
529 | mutex_lock(&mdev->mic_mutex); | ||
530 | |||
531 | ret = mic_copy_dp_entry(mvdev, argp, &type, &dd); | ||
532 | if (ret) { | ||
533 | mutex_unlock(&mdev->mic_mutex); | ||
534 | return ret; | ||
535 | } | ||
536 | |||
537 | mic_init_device_ctrl(mvdev, dd); | ||
538 | |||
539 | mvdev->dd = dd; | ||
540 | mvdev->virtio_id = type; | ||
541 | vqconfig = mic_vq_config(dd); | ||
542 | INIT_WORK(&mvdev->virtio_bh_work, mic_bh_handler); | ||
543 | |||
544 | for (i = 0; i < dd->num_vq; i++) { | ||
545 | struct mic_vringh *mvr = &mvdev->mvr[i]; | ||
546 | struct mic_vring *vr = &mvdev->mvr[i].vring; | ||
547 | num = le16_to_cpu(vqconfig[i].num); | ||
548 | mutex_init(&mvr->vr_mutex); | ||
549 | vr_size = PAGE_ALIGN(vring_size(num, MIC_VIRTIO_RING_ALIGN) + | ||
550 | sizeof(struct _mic_vring_info)); | ||
551 | vr->va = (void *) | ||
552 | __get_free_pages(GFP_KERNEL | __GFP_ZERO, | ||
553 | get_order(vr_size)); | ||
554 | if (!vr->va) { | ||
555 | ret = -ENOMEM; | ||
556 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | ||
557 | __func__, __LINE__, ret); | ||
558 | goto err; | ||
559 | } | ||
560 | vr->len = vr_size; | ||
561 | vr->info = vr->va + vring_size(num, MIC_VIRTIO_RING_ALIGN); | ||
562 | vr->info->magic = MIC_MAGIC + mvdev->virtio_id + i; | ||
563 | vqconfig[i].address = mic_map_single(mdev, | ||
564 | vr->va, vr_size); | ||
565 | if (mic_map_error(vqconfig[i].address)) { | ||
566 | free_pages((unsigned long)vr->va, get_order(vr_size)); | ||
567 | ret = -ENOMEM; | ||
568 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | ||
569 | __func__, __LINE__, ret); | ||
570 | goto err; | ||
571 | } | ||
572 | vqconfig[i].address = cpu_to_le64(vqconfig[i].address); | ||
573 | |||
574 | vring_init(&vr->vr, num, vr->va, MIC_VIRTIO_RING_ALIGN); | ||
575 | ret = vringh_init_kern(&mvr->vrh, | ||
576 | *(u32 *)mic_vq_features(mvdev->dd), num, false, | ||
577 | vr->vr.desc, vr->vr.avail, vr->vr.used); | ||
578 | if (ret) { | ||
579 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | ||
580 | __func__, __LINE__, ret); | ||
581 | goto err; | ||
582 | } | ||
583 | vringh_kiov_init(&mvr->riov, NULL, 0); | ||
584 | vringh_kiov_init(&mvr->wiov, NULL, 0); | ||
585 | mvr->head = USHRT_MAX; | ||
586 | mvr->mvdev = mvdev; | ||
587 | mvr->vrh.notify = mic_notify; | ||
588 | dev_dbg(mdev->sdev->parent, | ||
589 | "%s %d index %d va %p info %p vr_size 0x%x\n", | ||
590 | __func__, __LINE__, i, vr->va, vr->info, vr_size); | ||
591 | } | ||
592 | |||
593 | snprintf(irqname, sizeof(irqname), "mic%dvirtio%d", mdev->id, | ||
594 | mvdev->virtio_id); | ||
595 | mvdev->virtio_db = mic_next_db(mdev); | ||
596 | mvdev->virtio_cookie = mic_request_irq(mdev, mic_virtio_intr_handler, | ||
597 | irqname, mvdev, mvdev->virtio_db, MIC_INTR_DB); | ||
598 | if (IS_ERR(mvdev->virtio_cookie)) { | ||
599 | ret = PTR_ERR(mvdev->virtio_cookie); | ||
600 | dev_dbg(mdev->sdev->parent, "request irq failed\n"); | ||
601 | goto err; | ||
602 | } | ||
603 | |||
604 | mvdev->dc->c2h_vdev_db = mvdev->virtio_db; | ||
605 | |||
606 | list_add_tail(&mvdev->list, &mdev->vdev_list); | ||
607 | /* | ||
608 | * Order the type update with previous stores. This write barrier | ||
609 | * is paired with the corresponding read barrier before the uncached | ||
610 | * system memory read of the type, on the card while scanning the | ||
611 | * device page. | ||
612 | */ | ||
613 | smp_wmb(); | ||
614 | dd->type = type; | ||
615 | |||
616 | dev_dbg(mdev->sdev->parent, "Added virtio device id %d\n", dd->type); | ||
617 | |||
618 | db = bootparam->h2c_config_db; | ||
619 | if (db != -1) | ||
620 | mdev->ops->send_intr(mdev, db); | ||
621 | mutex_unlock(&mdev->mic_mutex); | ||
622 | return 0; | ||
623 | err: | ||
624 | vqconfig = mic_vq_config(dd); | ||
625 | for (j = 0; j < i; j++) { | ||
626 | struct mic_vringh *mvr = &mvdev->mvr[j]; | ||
627 | mic_unmap_single(mdev, le64_to_cpu(vqconfig[j].address), | ||
628 | mvr->vring.len); | ||
629 | free_pages((unsigned long)mvr->vring.va, | ||
630 | get_order(mvr->vring.len)); | ||
631 | } | ||
632 | mutex_unlock(&mdev->mic_mutex); | ||
633 | return ret; | ||
634 | } | ||
635 | |||
636 | void mic_virtio_del_device(struct mic_vdev *mvdev) | ||
637 | { | ||
638 | struct list_head *pos, *tmp; | ||
639 | struct mic_vdev *tmp_mvdev; | ||
640 | struct mic_device *mdev = mvdev->mdev; | ||
641 | DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wake); | ||
642 | int i, ret, retry = 100; | ||
643 | struct mic_vqconfig *vqconfig; | ||
644 | struct mic_bootparam *bootparam = mdev->dp; | ||
645 | s8 db; | ||
646 | |||
647 | mutex_lock(&mdev->mic_mutex); | ||
648 | db = bootparam->h2c_config_db; | ||
649 | if (db == -1) | ||
650 | goto skip_hot_remove; | ||
651 | dev_dbg(mdev->sdev->parent, | ||
652 | "Requesting hot remove id %d\n", mvdev->virtio_id); | ||
653 | mvdev->dc->config_change = MIC_VIRTIO_PARAM_DEV_REMOVE; | ||
654 | mdev->ops->send_intr(mdev, db); | ||
655 | for (i = retry; i--;) { | ||
656 | ret = wait_event_timeout(wake, | ||
657 | mvdev->dc->guest_ack, msecs_to_jiffies(100)); | ||
658 | if (ret) | ||
659 | break; | ||
660 | } | ||
661 | dev_dbg(mdev->sdev->parent, | ||
662 | "Device id %d config_change %d guest_ack %d\n", | ||
663 | mvdev->virtio_id, mvdev->dc->config_change, | ||
664 | mvdev->dc->guest_ack); | ||
665 | mvdev->dc->config_change = 0; | ||
666 | mvdev->dc->guest_ack = 0; | ||
667 | skip_hot_remove: | ||
668 | mic_free_irq(mdev, mvdev->virtio_cookie, mvdev); | ||
669 | flush_work(&mvdev->virtio_bh_work); | ||
670 | vqconfig = mic_vq_config(mvdev->dd); | ||
671 | for (i = 0; i < mvdev->dd->num_vq; i++) { | ||
672 | struct mic_vringh *mvr = &mvdev->mvr[i]; | ||
673 | vringh_kiov_cleanup(&mvr->riov); | ||
674 | vringh_kiov_cleanup(&mvr->wiov); | ||
675 | mic_unmap_single(mdev, le64_to_cpu(vqconfig[i].address), | ||
676 | mvr->vring.len); | ||
677 | free_pages((unsigned long)mvr->vring.va, | ||
678 | get_order(mvr->vring.len)); | ||
679 | } | ||
680 | |||
681 | list_for_each_safe(pos, tmp, &mdev->vdev_list) { | ||
682 | tmp_mvdev = list_entry(pos, struct mic_vdev, list); | ||
683 | if (tmp_mvdev == mvdev) { | ||
684 | list_del(pos); | ||
685 | dev_dbg(mdev->sdev->parent, | ||
686 | "Removing virtio device id %d\n", | ||
687 | mvdev->virtio_id); | ||
688 | break; | ||
689 | } | ||
690 | } | ||
691 | /* | ||
692 | * Order the type update with previous stores. This write barrier | ||
693 | * is paired with the corresponding read barrier before the uncached | ||
694 | * system memory read of the type, on the card while scanning the | ||
695 | * device page. | ||
696 | */ | ||
697 | smp_wmb(); | ||
698 | mvdev->dd->type = -1; | ||
699 | mutex_unlock(&mdev->mic_mutex); | ||
700 | } | ||
diff --git a/drivers/misc/mic/host/mic_virtio.h b/drivers/misc/mic/host/mic_virtio.h new file mode 100644 index 000000000000..184f3c84805b --- /dev/null +++ b/drivers/misc/mic/host/mic_virtio.h | |||
@@ -0,0 +1,138 @@ | |||
1 | /* | ||
2 | * Intel MIC Platform Software Stack (MPSS) | ||
3 | * | ||
4 | * Copyright(c) 2013 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 Host driver. | ||
19 | * | ||
20 | */ | ||
21 | #ifndef MIC_VIRTIO_H | ||
22 | #define MIC_VIRTIO_H | ||
23 | |||
24 | #include <linux/virtio_config.h> | ||
25 | #include <linux/mic_ioctl.h> | ||
26 | |||
27 | /* | ||
28 | * Note on endianness. | ||
29 | * 1. Host can be both BE or LE | ||
30 | * 2. Guest/card is LE. Host uses le_to_cpu to access desc/avail | ||
31 | * rings and ioreadXX/iowriteXX to access used ring. | ||
32 | * 3. Device page exposed by host to guest contains LE values. Guest | ||
33 | * accesses these using ioreadXX/iowriteXX etc. This way in general we | ||
34 | * obey the virtio spec according to which guest works with native | ||
35 | * endianness and host is aware of guest endianness and does all | ||
36 | * required endianness conversion. | ||
37 | * 4. Data provided from user space to guest (in ADD_DEVICE and | ||
38 | * CONFIG_CHANGE ioctl's) is not interpreted by the driver and should be | ||
39 | * in guest endianness. | ||
40 | */ | ||
41 | |||
42 | /** | ||
43 | * struct mic_vringh - Virtio ring host information. | ||
44 | * | ||
45 | * @vring: The MIC vring used for setting up user space mappings. | ||
46 | * @vrh: The host VRINGH used for accessing the card vrings. | ||
47 | * @riov: The VRINGH read kernel IOV. | ||
48 | * @wiov: The VRINGH write kernel IOV. | ||
49 | * @head: The VRINGH head index address passed to vringh_getdesc_kern(..). | ||
50 | * @vr_mutex: Mutex for synchronizing access to the VRING. | ||
51 | * @mvdev: Back pointer to MIC virtio device for vringh_notify(..). | ||
52 | */ | ||
53 | struct mic_vringh { | ||
54 | struct mic_vring vring; | ||
55 | struct vringh vrh; | ||
56 | struct vringh_kiov riov; | ||
57 | struct vringh_kiov wiov; | ||
58 | u16 head; | ||
59 | struct mutex vr_mutex; | ||
60 | struct mic_vdev *mvdev; | ||
61 | }; | ||
62 | |||
63 | /** | ||
64 | * struct mic_vdev - Host information for a card Virtio device. | ||
65 | * | ||
66 | * @virtio_id - Virtio device id. | ||
67 | * @waitq - Waitqueue to allow ring3 apps to poll. | ||
68 | * @mdev - Back pointer to host MIC device. | ||
69 | * @poll_wake - Used for waking up threads blocked in poll. | ||
70 | * @out_bytes - Debug stats for number of bytes copied from host to card. | ||
71 | * @in_bytes - Debug stats for number of bytes copied from card to host. | ||
72 | * @mvr - Store per VRING data structures. | ||
73 | * @virtio_bh_work - Work struct used to schedule virtio bottom half handling. | ||
74 | * @dd - Virtio device descriptor. | ||
75 | * @dc - Virtio device control fields. | ||
76 | * @list - List of Virtio devices. | ||
77 | * @virtio_db - The doorbell used by the card to interrupt the host. | ||
78 | * @virtio_cookie - The cookie returned while requesting interrupts. | ||
79 | */ | ||
80 | struct mic_vdev { | ||
81 | int virtio_id; | ||
82 | wait_queue_head_t waitq; | ||
83 | struct mic_device *mdev; | ||
84 | int poll_wake; | ||
85 | unsigned long out_bytes; | ||
86 | unsigned long in_bytes; | ||
87 | struct mic_vringh mvr[MIC_MAX_VRINGS]; | ||
88 | struct work_struct virtio_bh_work; | ||
89 | struct mic_device_desc *dd; | ||
90 | struct mic_device_ctrl *dc; | ||
91 | struct list_head list; | ||
92 | int virtio_db; | ||
93 | struct mic_irq *virtio_cookie; | ||
94 | }; | ||
95 | |||
96 | void mic_virtio_uninit(struct mic_device *mdev); | ||
97 | int mic_virtio_add_device(struct mic_vdev *mvdev, | ||
98 | void __user *argp); | ||
99 | void mic_virtio_del_device(struct mic_vdev *mvdev); | ||
100 | int mic_virtio_config_change(struct mic_vdev *mvdev, | ||
101 | void __user *argp); | ||
102 | int mic_virtio_copy_desc(struct mic_vdev *mvdev, | ||
103 | struct mic_copy_desc *request); | ||
104 | void mic_virtio_reset_devices(struct mic_device *mdev); | ||
105 | void mic_bh_handler(struct work_struct *work); | ||
106 | |||
107 | /* Helper API to obtain the MIC PCIe device */ | ||
108 | static inline struct device *mic_dev(struct mic_vdev *mvdev) | ||
109 | { | ||
110 | return mvdev->mdev->sdev->parent; | ||
111 | } | ||
112 | |||
113 | /* Helper API to check if a virtio device is initialized */ | ||
114 | static inline int mic_vdev_inited(struct mic_vdev *mvdev) | ||
115 | { | ||
116 | /* Device has not been created yet */ | ||
117 | if (!mvdev->dd || !mvdev->dd->type) { | ||
118 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | ||
119 | __func__, __LINE__, -EINVAL); | ||
120 | return -EINVAL; | ||
121 | } | ||
122 | |||
123 | /* Device has been removed/deleted */ | ||
124 | if (mvdev->dd->type == -1) { | ||
125 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | ||
126 | __func__, __LINE__, -ENODEV); | ||
127 | return -ENODEV; | ||
128 | } | ||
129 | |||
130 | return 0; | ||
131 | } | ||
132 | |||
133 | /* Helper API to check if a virtio device is running */ | ||
134 | static inline bool mic_vdevup(struct mic_vdev *mvdev) | ||
135 | { | ||
136 | return !!mvdev->dd->status; | ||
137 | } | ||
138 | #endif | ||
diff --git a/drivers/misc/mic/host/mic_x100.c b/drivers/misc/mic/host/mic_x100.c new file mode 100644 index 000000000000..81e9541b784c --- /dev/null +++ b/drivers/misc/mic/host/mic_x100.c | |||
@@ -0,0 +1,570 @@ | |||
1 | /* | ||
2 | * Intel MIC Platform Software Stack (MPSS) | ||
3 | * | ||
4 | * Copyright(c) 2013 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 Host driver. | ||
19 | * | ||
20 | */ | ||
21 | #include <linux/fs.h> | ||
22 | #include <linux/pci.h> | ||
23 | #include <linux/sched.h> | ||
24 | #include <linux/firmware.h> | ||
25 | #include <linux/delay.h> | ||
26 | |||
27 | #include "../common/mic_dev.h" | ||
28 | #include "mic_device.h" | ||
29 | #include "mic_x100.h" | ||
30 | #include "mic_smpt.h" | ||
31 | |||
32 | /** | ||
33 | * mic_x100_write_spad - write to the scratchpad register | ||
34 | * @mdev: pointer to mic_device instance | ||
35 | * @idx: index to the scratchpad register, 0 based | ||
36 | * @val: the data value to put into the register | ||
37 | * | ||
38 | * This function allows writing of a 32bit value to the indexed scratchpad | ||
39 | * register. | ||
40 | * | ||
41 | * RETURNS: none. | ||
42 | */ | ||
43 | static void | ||
44 | mic_x100_write_spad(struct mic_device *mdev, unsigned int idx, u32 val) | ||
45 | { | ||
46 | dev_dbg(mdev->sdev->parent, "Writing 0x%x to scratch pad index %d\n", | ||
47 | val, idx); | ||
48 | mic_mmio_write(&mdev->mmio, val, | ||
49 | MIC_X100_SBOX_BASE_ADDRESS + | ||
50 | MIC_X100_SBOX_SPAD0 + idx * 4); | ||
51 | } | ||
52 | |||
53 | /** | ||
54 | * mic_x100_read_spad - read from the scratchpad register | ||
55 | * @mdev: pointer to mic_device instance | ||
56 | * @idx: index to scratchpad register, 0 based | ||
57 | * | ||
58 | * This function allows reading of the 32bit scratchpad register. | ||
59 | * | ||
60 | * RETURNS: An appropriate -ERRNO error value on error, or zero for success. | ||
61 | */ | ||
62 | static u32 | ||
63 | mic_x100_read_spad(struct mic_device *mdev, unsigned int idx) | ||
64 | { | ||
65 | u32 val = mic_mmio_read(&mdev->mmio, | ||
66 | MIC_X100_SBOX_BASE_ADDRESS + | ||
67 | MIC_X100_SBOX_SPAD0 + idx * 4); | ||
68 | |||
69 | dev_dbg(mdev->sdev->parent, | ||
70 | "Reading 0x%x from scratch pad index %d\n", val, idx); | ||
71 | return val; | ||
72 | } | ||
73 | |||
74 | /** | ||
75 | * mic_x100_enable_interrupts - Enable interrupts. | ||
76 | * @mdev: pointer to mic_device instance | ||
77 | */ | ||
78 | static void mic_x100_enable_interrupts(struct mic_device *mdev) | ||
79 | { | ||
80 | u32 reg; | ||
81 | struct mic_mw *mw = &mdev->mmio; | ||
82 | u32 sice0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SICE0; | ||
83 | u32 siac0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SIAC0; | ||
84 | |||
85 | reg = mic_mmio_read(mw, sice0); | ||
86 | reg |= MIC_X100_SBOX_DBR_BITS(0xf) | MIC_X100_SBOX_DMA_BITS(0xff); | ||
87 | mic_mmio_write(mw, reg, sice0); | ||
88 | |||
89 | /* | ||
90 | * Enable auto-clear when enabling interrupts. Applicable only for | ||
91 | * MSI-x. Legacy and MSI mode cannot have auto-clear enabled. | ||
92 | */ | ||
93 | if (mdev->irq_info.num_vectors > 1) { | ||
94 | reg = mic_mmio_read(mw, siac0); | ||
95 | reg |= MIC_X100_SBOX_DBR_BITS(0xf) | | ||
96 | MIC_X100_SBOX_DMA_BITS(0xff); | ||
97 | mic_mmio_write(mw, reg, siac0); | ||
98 | } | ||
99 | } | ||
100 | |||
101 | /** | ||
102 | * mic_x100_disable_interrupts - Disable interrupts. | ||
103 | * @mdev: pointer to mic_device instance | ||
104 | */ | ||
105 | static void mic_x100_disable_interrupts(struct mic_device *mdev) | ||
106 | { | ||
107 | u32 reg; | ||
108 | struct mic_mw *mw = &mdev->mmio; | ||
109 | u32 sice0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SICE0; | ||
110 | u32 siac0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SIAC0; | ||
111 | u32 sicc0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SICC0; | ||
112 | |||
113 | reg = mic_mmio_read(mw, sice0); | ||
114 | mic_mmio_write(mw, reg, sicc0); | ||
115 | |||
116 | if (mdev->irq_info.num_vectors > 1) { | ||
117 | reg = mic_mmio_read(mw, siac0); | ||
118 | reg &= ~(MIC_X100_SBOX_DBR_BITS(0xf) | | ||
119 | MIC_X100_SBOX_DMA_BITS(0xff)); | ||
120 | mic_mmio_write(mw, reg, siac0); | ||
121 | } | ||
122 | } | ||
123 | |||
124 | /** | ||
125 | * mic_x100_send_sbox_intr - Send an MIC_X100_SBOX interrupt to MIC. | ||
126 | * @mdev: pointer to mic_device instance | ||
127 | */ | ||
128 | static void mic_x100_send_sbox_intr(struct mic_device *mdev, | ||
129 | int doorbell) | ||
130 | { | ||
131 | struct mic_mw *mw = &mdev->mmio; | ||
132 | u64 apic_icr_offset = MIC_X100_SBOX_APICICR0 + doorbell * 8; | ||
133 | u32 apicicr_low = mic_mmio_read(mw, MIC_X100_SBOX_BASE_ADDRESS + | ||
134 | apic_icr_offset); | ||
135 | |||
136 | /* for MIC we need to make sure we "hit" the send_icr bit (13) */ | ||
137 | apicicr_low = (apicicr_low | (1 << 13)); | ||
138 | |||
139 | /* Ensure that the interrupt is ordered w.r.t. previous stores. */ | ||
140 | wmb(); | ||
141 | mic_mmio_write(mw, apicicr_low, | ||
142 | MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset); | ||
143 | } | ||
144 | |||
145 | /** | ||
146 | * mic_x100_send_rdmasr_intr - Send an RDMASR interrupt to MIC. | ||
147 | * @mdev: pointer to mic_device instance | ||
148 | */ | ||
149 | static void mic_x100_send_rdmasr_intr(struct mic_device *mdev, | ||
150 | int doorbell) | ||
151 | { | ||
152 | int rdmasr_offset = MIC_X100_SBOX_RDMASR0 + (doorbell << 2); | ||
153 | /* Ensure that the interrupt is ordered w.r.t. previous stores. */ | ||
154 | wmb(); | ||
155 | mic_mmio_write(&mdev->mmio, 0, | ||
156 | MIC_X100_SBOX_BASE_ADDRESS + rdmasr_offset); | ||
157 | } | ||
158 | |||
159 | /** | ||
160 | * __mic_x100_send_intr - Send interrupt to MIC. | ||
161 | * @mdev: pointer to mic_device instance | ||
162 | * @doorbell: doorbell number. | ||
163 | */ | ||
164 | static void mic_x100_send_intr(struct mic_device *mdev, int doorbell) | ||
165 | { | ||
166 | int rdmasr_db; | ||
167 | if (doorbell < MIC_X100_NUM_SBOX_IRQ) { | ||
168 | mic_x100_send_sbox_intr(mdev, doorbell); | ||
169 | } else { | ||
170 | rdmasr_db = doorbell - MIC_X100_NUM_SBOX_IRQ + | ||
171 | MIC_X100_RDMASR_IRQ_BASE; | ||
172 | mic_x100_send_rdmasr_intr(mdev, rdmasr_db); | ||
173 | } | ||
174 | } | ||
175 | |||
176 | /** | ||
177 | * mic_ack_interrupt - Device specific interrupt handling. | ||
178 | * @mdev: pointer to mic_device instance | ||
179 | * | ||
180 | * Returns: bitmask of doorbell events triggered. | ||
181 | */ | ||
182 | static u32 mic_x100_ack_interrupt(struct mic_device *mdev) | ||
183 | { | ||
184 | u32 reg = 0; | ||
185 | struct mic_mw *mw = &mdev->mmio; | ||
186 | u32 sicr0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SICR0; | ||
187 | |||
188 | /* Clear pending bit array. */ | ||
189 | if (MIC_A0_STEP == mdev->stepping) | ||
190 | mic_mmio_write(mw, 1, MIC_X100_SBOX_BASE_ADDRESS + | ||
191 | MIC_X100_SBOX_MSIXPBACR); | ||
192 | |||
193 | if (mdev->irq_info.num_vectors <= 1) { | ||
194 | reg = mic_mmio_read(mw, sicr0); | ||
195 | |||
196 | if (unlikely(!reg)) | ||
197 | goto done; | ||
198 | |||
199 | mic_mmio_write(mw, reg, sicr0); | ||
200 | } | ||
201 | |||
202 | if (mdev->stepping >= MIC_B0_STEP) | ||
203 | mdev->intr_ops->enable_interrupts(mdev); | ||
204 | done: | ||
205 | return reg; | ||
206 | } | ||
207 | |||
208 | /** | ||
209 | * mic_x100_hw_intr_init - Initialize h/w specific interrupt | ||
210 | * information. | ||
211 | * @mdev: pointer to mic_device instance | ||
212 | */ | ||
213 | static void mic_x100_hw_intr_init(struct mic_device *mdev) | ||
214 | { | ||
215 | mdev->intr_info = (struct mic_intr_info *)mic_x100_intr_init; | ||
216 | } | ||
217 | |||
218 | /** | ||
219 | * mic_x100_read_msi_to_src_map - read from the MSI mapping registers | ||
220 | * @mdev: pointer to mic_device instance | ||
221 | * @idx: index to the mapping register, 0 based | ||
222 | * | ||
223 | * This function allows reading of the 32bit MSI mapping register. | ||
224 | * | ||
225 | * RETURNS: The value in the register. | ||
226 | */ | ||
227 | static u32 | ||
228 | mic_x100_read_msi_to_src_map(struct mic_device *mdev, int idx) | ||
229 | { | ||
230 | return mic_mmio_read(&mdev->mmio, | ||
231 | MIC_X100_SBOX_BASE_ADDRESS + | ||
232 | MIC_X100_SBOX_MXAR0 + idx * 4); | ||
233 | } | ||
234 | |||
235 | /** | ||
236 | * mic_x100_program_msi_to_src_map - program the MSI mapping registers | ||
237 | * @mdev: pointer to mic_device instance | ||
238 | * @idx: index to the mapping register, 0 based | ||
239 | * @offset: The bit offset in the register that needs to be updated. | ||
240 | * @set: boolean specifying if the bit in the specified offset needs | ||
241 | * to be set or cleared. | ||
242 | * | ||
243 | * RETURNS: None. | ||
244 | */ | ||
245 | static void | ||
246 | mic_x100_program_msi_to_src_map(struct mic_device *mdev, | ||
247 | int idx, int offset, bool set) | ||
248 | { | ||
249 | unsigned long reg; | ||
250 | struct mic_mw *mw = &mdev->mmio; | ||
251 | u32 mxar = MIC_X100_SBOX_BASE_ADDRESS + | ||
252 | MIC_X100_SBOX_MXAR0 + idx * 4; | ||
253 | |||
254 | reg = mic_mmio_read(mw, mxar); | ||
255 | if (set) | ||
256 | __set_bit(offset, ®); | ||
257 | else | ||
258 | __clear_bit(offset, ®); | ||
259 | mic_mmio_write(mw, reg, mxar); | ||
260 | } | ||
261 | |||
262 | /* | ||
263 | * mic_x100_reset_fw_ready - Reset Firmware ready status field. | ||
264 | * @mdev: pointer to mic_device instance | ||
265 | */ | ||
266 | static void mic_x100_reset_fw_ready(struct mic_device *mdev) | ||
267 | { | ||
268 | mdev->ops->write_spad(mdev, MIC_X100_DOWNLOAD_INFO, 0); | ||
269 | } | ||
270 | |||
271 | /* | ||
272 | * mic_x100_is_fw_ready - Check if firmware is ready. | ||
273 | * @mdev: pointer to mic_device instance | ||
274 | */ | ||
275 | static bool mic_x100_is_fw_ready(struct mic_device *mdev) | ||
276 | { | ||
277 | u32 scratch2 = mdev->ops->read_spad(mdev, MIC_X100_DOWNLOAD_INFO); | ||
278 | return MIC_X100_SPAD2_DOWNLOAD_STATUS(scratch2) ? true : false; | ||
279 | } | ||
280 | |||
281 | /** | ||
282 | * mic_x100_get_apic_id - Get bootstrap APIC ID. | ||
283 | * @mdev: pointer to mic_device instance | ||
284 | */ | ||
285 | static u32 mic_x100_get_apic_id(struct mic_device *mdev) | ||
286 | { | ||
287 | u32 scratch2 = 0; | ||
288 | |||
289 | scratch2 = mdev->ops->read_spad(mdev, MIC_X100_DOWNLOAD_INFO); | ||
290 | return MIC_X100_SPAD2_APIC_ID(scratch2); | ||
291 | } | ||
292 | |||
293 | /** | ||
294 | * mic_x100_send_firmware_intr - Send an interrupt to the firmware on MIC. | ||
295 | * @mdev: pointer to mic_device instance | ||
296 | */ | ||
297 | static void mic_x100_send_firmware_intr(struct mic_device *mdev) | ||
298 | { | ||
299 | u32 apicicr_low; | ||
300 | u64 apic_icr_offset = MIC_X100_SBOX_APICICR7; | ||
301 | int vector = MIC_X100_BSP_INTERRUPT_VECTOR; | ||
302 | struct mic_mw *mw = &mdev->mmio; | ||
303 | |||
304 | /* | ||
305 | * For MIC we need to make sure we "hit" | ||
306 | * the send_icr bit (13). | ||
307 | */ | ||
308 | apicicr_low = (vector | (1 << 13)); | ||
309 | |||
310 | mic_mmio_write(mw, mic_x100_get_apic_id(mdev), | ||
311 | MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset + 4); | ||
312 | |||
313 | /* Ensure that the interrupt is ordered w.r.t. previous stores. */ | ||
314 | wmb(); | ||
315 | mic_mmio_write(mw, apicicr_low, | ||
316 | MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset); | ||
317 | } | ||
318 | |||
319 | /** | ||
320 | * mic_x100_hw_reset - Reset the MIC device. | ||
321 | * @mdev: pointer to mic_device instance | ||
322 | */ | ||
323 | static void mic_x100_hw_reset(struct mic_device *mdev) | ||
324 | { | ||
325 | u32 reset_reg; | ||
326 | u32 rgcr = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_RGCR; | ||
327 | struct mic_mw *mw = &mdev->mmio; | ||
328 | |||
329 | /* Ensure that the reset is ordered w.r.t. previous loads and stores */ | ||
330 | mb(); | ||
331 | /* Trigger reset */ | ||
332 | reset_reg = mic_mmio_read(mw, rgcr); | ||
333 | reset_reg |= 0x1; | ||
334 | mic_mmio_write(mw, reset_reg, rgcr); | ||
335 | /* | ||
336 | * It seems we really want to delay at least 1 second | ||
337 | * after touching reset to prevent a lot of problems. | ||
338 | */ | ||
339 | msleep(1000); | ||
340 | } | ||
341 | |||
342 | /** | ||
343 | * mic_x100_load_command_line - Load command line to MIC. | ||
344 | * @mdev: pointer to mic_device instance | ||
345 | * @fw: the firmware image | ||
346 | * | ||
347 | * RETURNS: An appropriate -ERRNO error value on error, or zero for success. | ||
348 | */ | ||
349 | static int | ||
350 | mic_x100_load_command_line(struct mic_device *mdev, const struct firmware *fw) | ||
351 | { | ||
352 | u32 len = 0; | ||
353 | u32 boot_mem; | ||
354 | char *buf; | ||
355 | void __iomem *cmd_line_va = mdev->aper.va + mdev->bootaddr + fw->size; | ||
356 | #define CMDLINE_SIZE 2048 | ||
357 | |||
358 | boot_mem = mdev->aper.len >> 20; | ||
359 | buf = kzalloc(CMDLINE_SIZE, GFP_KERNEL); | ||
360 | if (!buf) { | ||
361 | dev_err(mdev->sdev->parent, | ||
362 | "%s %d allocation failed\n", __func__, __LINE__); | ||
363 | return -ENOMEM; | ||
364 | } | ||
365 | len += snprintf(buf, CMDLINE_SIZE - len, | ||
366 | " mem=%dM", boot_mem); | ||
367 | if (mdev->cmdline) | ||
368 | snprintf(buf + len, CMDLINE_SIZE - len, " %s", mdev->cmdline); | ||
369 | memcpy_toio(cmd_line_va, buf, strlen(buf) + 1); | ||
370 | kfree(buf); | ||
371 | return 0; | ||
372 | } | ||
373 | |||
374 | /** | ||
375 | * mic_x100_load_ramdisk - Load ramdisk to MIC. | ||
376 | * @mdev: pointer to mic_device instance | ||
377 | * | ||
378 | * RETURNS: An appropriate -ERRNO error value on error, or zero for success. | ||
379 | */ | ||
380 | static int | ||
381 | mic_x100_load_ramdisk(struct mic_device *mdev) | ||
382 | { | ||
383 | const struct firmware *fw; | ||
384 | int rc; | ||
385 | struct boot_params __iomem *bp = mdev->aper.va + mdev->bootaddr; | ||
386 | |||
387 | rc = request_firmware(&fw, | ||
388 | mdev->ramdisk, mdev->sdev->parent); | ||
389 | if (rc < 0) { | ||
390 | dev_err(mdev->sdev->parent, | ||
391 | "ramdisk request_firmware failed: %d %s\n", | ||
392 | rc, mdev->ramdisk); | ||
393 | goto error; | ||
394 | } | ||
395 | /* | ||
396 | * Typically the bootaddr for card OS is 64M | ||
397 | * so copy over the ramdisk @ 128M. | ||
398 | */ | ||
399 | memcpy_toio(mdev->aper.va + (mdev->bootaddr << 1), fw->data, fw->size); | ||
400 | iowrite32(cpu_to_le32(mdev->bootaddr << 1), &bp->hdr.ramdisk_image); | ||
401 | iowrite32(cpu_to_le32(fw->size), &bp->hdr.ramdisk_size); | ||
402 | release_firmware(fw); | ||
403 | error: | ||
404 | return rc; | ||
405 | } | ||
406 | |||
407 | /** | ||
408 | * mic_x100_get_boot_addr - Get MIC boot address. | ||
409 | * @mdev: pointer to mic_device instance | ||
410 | * | ||
411 | * This function is called during firmware load to determine | ||
412 | * the address at which the OS should be downloaded in card | ||
413 | * memory i.e. GDDR. | ||
414 | * RETURNS: An appropriate -ERRNO error value on error, or zero for success. | ||
415 | */ | ||
416 | static int | ||
417 | mic_x100_get_boot_addr(struct mic_device *mdev) | ||
418 | { | ||
419 | u32 scratch2, boot_addr; | ||
420 | int rc = 0; | ||
421 | |||
422 | scratch2 = mdev->ops->read_spad(mdev, MIC_X100_DOWNLOAD_INFO); | ||
423 | boot_addr = MIC_X100_SPAD2_DOWNLOAD_ADDR(scratch2); | ||
424 | dev_dbg(mdev->sdev->parent, "%s %d boot_addr 0x%x\n", | ||
425 | __func__, __LINE__, boot_addr); | ||
426 | if (boot_addr > (1 << 31)) { | ||
427 | dev_err(mdev->sdev->parent, | ||
428 | "incorrect bootaddr 0x%x\n", | ||
429 | boot_addr); | ||
430 | rc = -EINVAL; | ||
431 | goto error; | ||
432 | } | ||
433 | mdev->bootaddr = boot_addr; | ||
434 | error: | ||
435 | return rc; | ||
436 | } | ||
437 | |||
438 | /** | ||
439 | * mic_x100_load_firmware - Load firmware to MIC. | ||
440 | * @mdev: pointer to mic_device instance | ||
441 | * @buf: buffer containing boot string including firmware/ramdisk path. | ||
442 | * | ||
443 | * RETURNS: An appropriate -ERRNO error value on error, or zero for success. | ||
444 | */ | ||
445 | static int | ||
446 | mic_x100_load_firmware(struct mic_device *mdev, const char *buf) | ||
447 | { | ||
448 | int rc; | ||
449 | const struct firmware *fw; | ||
450 | |||
451 | rc = mic_x100_get_boot_addr(mdev); | ||
452 | if (rc) | ||
453 | goto error; | ||
454 | /* load OS */ | ||
455 | rc = request_firmware(&fw, mdev->firmware, mdev->sdev->parent); | ||
456 | if (rc < 0) { | ||
457 | dev_err(mdev->sdev->parent, | ||
458 | "ramdisk request_firmware failed: %d %s\n", | ||
459 | rc, mdev->firmware); | ||
460 | goto error; | ||
461 | } | ||
462 | if (mdev->bootaddr > mdev->aper.len - fw->size) { | ||
463 | rc = -EINVAL; | ||
464 | dev_err(mdev->sdev->parent, "%s %d rc %d bootaddr 0x%x\n", | ||
465 | __func__, __LINE__, rc, mdev->bootaddr); | ||
466 | release_firmware(fw); | ||
467 | goto error; | ||
468 | } | ||
469 | memcpy_toio(mdev->aper.va + mdev->bootaddr, fw->data, fw->size); | ||
470 | mdev->ops->write_spad(mdev, MIC_X100_FW_SIZE, fw->size); | ||
471 | if (!strcmp(mdev->bootmode, "elf")) | ||
472 | goto done; | ||
473 | /* load command line */ | ||
474 | rc = mic_x100_load_command_line(mdev, fw); | ||
475 | if (rc) { | ||
476 | dev_err(mdev->sdev->parent, "%s %d rc %d\n", | ||
477 | __func__, __LINE__, rc); | ||
478 | goto error; | ||
479 | } | ||
480 | release_firmware(fw); | ||
481 | /* load ramdisk */ | ||
482 | if (mdev->ramdisk) | ||
483 | rc = mic_x100_load_ramdisk(mdev); | ||
484 | error: | ||
485 | dev_dbg(mdev->sdev->parent, "%s %d rc %d\n", __func__, __LINE__, rc); | ||
486 | done: | ||
487 | return rc; | ||
488 | } | ||
489 | |||
490 | /** | ||
491 | * mic_x100_get_postcode - Get postcode status from firmware. | ||
492 | * @mdev: pointer to mic_device instance | ||
493 | * | ||
494 | * RETURNS: postcode. | ||
495 | */ | ||
496 | static u32 mic_x100_get_postcode(struct mic_device *mdev) | ||
497 | { | ||
498 | return mic_mmio_read(&mdev->mmio, MIC_X100_POSTCODE); | ||
499 | } | ||
500 | |||
501 | /** | ||
502 | * mic_x100_smpt_set - Update an SMPT entry with a DMA address. | ||
503 | * @mdev: pointer to mic_device instance | ||
504 | * | ||
505 | * RETURNS: none. | ||
506 | */ | ||
507 | static void | ||
508 | mic_x100_smpt_set(struct mic_device *mdev, dma_addr_t dma_addr, u8 index) | ||
509 | { | ||
510 | #define SNOOP_ON (0 << 0) | ||
511 | #define SNOOP_OFF (1 << 0) | ||
512 | /* | ||
513 | * Sbox Smpt Reg Bits: | ||
514 | * Bits 31:2 Host address | ||
515 | * Bits 1 RSVD | ||
516 | * Bits 0 No snoop | ||
517 | */ | ||
518 | #define BUILD_SMPT(NO_SNOOP, HOST_ADDR) \ | ||
519 | (u32)(((HOST_ADDR) << 2) | ((NO_SNOOP) & 0x01)) | ||
520 | |||
521 | uint32_t smpt_reg_val = BUILD_SMPT(SNOOP_ON, | ||
522 | dma_addr >> mdev->smpt->info.page_shift); | ||
523 | mic_mmio_write(&mdev->mmio, smpt_reg_val, | ||
524 | MIC_X100_SBOX_BASE_ADDRESS + | ||
525 | MIC_X100_SBOX_SMPT00 + (4 * index)); | ||
526 | } | ||
527 | |||
528 | /** | ||
529 | * mic_x100_smpt_hw_init - Initialize SMPT X100 specific fields. | ||
530 | * @mdev: pointer to mic_device instance | ||
531 | * | ||
532 | * RETURNS: none. | ||
533 | */ | ||
534 | static void mic_x100_smpt_hw_init(struct mic_device *mdev) | ||
535 | { | ||
536 | struct mic_smpt_hw_info *info = &mdev->smpt->info; | ||
537 | |||
538 | info->num_reg = 32; | ||
539 | info->page_shift = 34; | ||
540 | info->page_size = (1ULL << info->page_shift); | ||
541 | info->base = 0x8000000000ULL; | ||
542 | } | ||
543 | |||
544 | struct mic_smpt_ops mic_x100_smpt_ops = { | ||
545 | .init = mic_x100_smpt_hw_init, | ||
546 | .set = mic_x100_smpt_set, | ||
547 | }; | ||
548 | |||
549 | struct mic_hw_ops mic_x100_ops = { | ||
550 | .aper_bar = MIC_X100_APER_BAR, | ||
551 | .mmio_bar = MIC_X100_MMIO_BAR, | ||
552 | .read_spad = mic_x100_read_spad, | ||
553 | .write_spad = mic_x100_write_spad, | ||
554 | .send_intr = mic_x100_send_intr, | ||
555 | .ack_interrupt = mic_x100_ack_interrupt, | ||
556 | .reset = mic_x100_hw_reset, | ||
557 | .reset_fw_ready = mic_x100_reset_fw_ready, | ||
558 | .is_fw_ready = mic_x100_is_fw_ready, | ||
559 | .send_firmware_intr = mic_x100_send_firmware_intr, | ||
560 | .load_mic_fw = mic_x100_load_firmware, | ||
561 | .get_postcode = mic_x100_get_postcode, | ||
562 | }; | ||
563 | |||
564 | struct mic_hw_intr_ops mic_x100_intr_ops = { | ||
565 | .intr_init = mic_x100_hw_intr_init, | ||
566 | .enable_interrupts = mic_x100_enable_interrupts, | ||
567 | .disable_interrupts = mic_x100_disable_interrupts, | ||
568 | .program_msi_to_src_map = mic_x100_program_msi_to_src_map, | ||
569 | .read_msi_to_src_map = mic_x100_read_msi_to_src_map, | ||
570 | }; | ||
diff --git a/drivers/misc/mic/host/mic_x100.h b/drivers/misc/mic/host/mic_x100.h new file mode 100644 index 000000000000..8b7daa182e54 --- /dev/null +++ b/drivers/misc/mic/host/mic_x100.h | |||
@@ -0,0 +1,98 @@ | |||
1 | /* | ||
2 | * Intel MIC Platform Software Stack (MPSS) | ||
3 | * | ||
4 | * Copyright(c) 2013 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 Host driver. | ||
19 | * | ||
20 | */ | ||
21 | #ifndef _MIC_X100_HW_H_ | ||
22 | #define _MIC_X100_HW_H_ | ||
23 | |||
24 | #define MIC_X100_PCI_DEVICE_2250 0x2250 | ||
25 | #define MIC_X100_PCI_DEVICE_2251 0x2251 | ||
26 | #define MIC_X100_PCI_DEVICE_2252 0x2252 | ||
27 | #define MIC_X100_PCI_DEVICE_2253 0x2253 | ||
28 | #define MIC_X100_PCI_DEVICE_2254 0x2254 | ||
29 | #define MIC_X100_PCI_DEVICE_2255 0x2255 | ||
30 | #define MIC_X100_PCI_DEVICE_2256 0x2256 | ||
31 | #define MIC_X100_PCI_DEVICE_2257 0x2257 | ||
32 | #define MIC_X100_PCI_DEVICE_2258 0x2258 | ||
33 | #define MIC_X100_PCI_DEVICE_2259 0x2259 | ||
34 | #define MIC_X100_PCI_DEVICE_225a 0x225a | ||
35 | #define MIC_X100_PCI_DEVICE_225b 0x225b | ||
36 | #define MIC_X100_PCI_DEVICE_225c 0x225c | ||
37 | #define MIC_X100_PCI_DEVICE_225d 0x225d | ||
38 | #define MIC_X100_PCI_DEVICE_225e 0x225e | ||
39 | |||
40 | #define MIC_X100_APER_BAR 0 | ||
41 | #define MIC_X100_MMIO_BAR 4 | ||
42 | |||
43 | #define MIC_X100_SBOX_BASE_ADDRESS 0x00010000 | ||
44 | #define MIC_X100_SBOX_SPAD0 0x0000AB20 | ||
45 | #define MIC_X100_SBOX_SICR0_DBR(x) ((x) & 0xf) | ||
46 | #define MIC_X100_SBOX_SICR0_DMA(x) (((x) >> 8) & 0xff) | ||
47 | #define MIC_X100_SBOX_SICE0_DBR(x) ((x) & 0xf) | ||
48 | #define MIC_X100_SBOX_DBR_BITS(x) ((x) & 0xf) | ||
49 | #define MIC_X100_SBOX_SICE0_DMA(x) (((x) >> 8) & 0xff) | ||
50 | #define MIC_X100_SBOX_DMA_BITS(x) (((x) & 0xff) << 8) | ||
51 | |||
52 | #define MIC_X100_SBOX_APICICR0 0x0000A9D0 | ||
53 | #define MIC_X100_SBOX_SICR0 0x00009004 | ||
54 | #define MIC_X100_SBOX_SICE0 0x0000900C | ||
55 | #define MIC_X100_SBOX_SICC0 0x00009010 | ||
56 | #define MIC_X100_SBOX_SIAC0 0x00009014 | ||
57 | #define MIC_X100_SBOX_MSIXPBACR 0x00009084 | ||
58 | #define MIC_X100_SBOX_MXAR0 0x00009044 | ||
59 | #define MIC_X100_SBOX_SMPT00 0x00003100 | ||
60 | #define MIC_X100_SBOX_RDMASR0 0x0000B180 | ||
61 | |||
62 | #define MIC_X100_DOORBELL_IDX_START 0 | ||
63 | #define MIC_X100_NUM_DOORBELL 4 | ||
64 | #define MIC_X100_DMA_IDX_START 8 | ||
65 | #define MIC_X100_NUM_DMA 8 | ||
66 | #define MIC_X100_ERR_IDX_START 30 | ||
67 | #define MIC_X100_NUM_ERR 1 | ||
68 | |||
69 | #define MIC_X100_NUM_SBOX_IRQ 8 | ||
70 | #define MIC_X100_NUM_RDMASR_IRQ 8 | ||
71 | #define MIC_X100_RDMASR_IRQ_BASE 17 | ||
72 | #define MIC_X100_SPAD2_DOWNLOAD_STATUS(x) ((x) & 0x1) | ||
73 | #define MIC_X100_SPAD2_APIC_ID(x) (((x) >> 1) & 0x1ff) | ||
74 | #define MIC_X100_SPAD2_DOWNLOAD_ADDR(x) ((x) & 0xfffff000) | ||
75 | #define MIC_X100_SBOX_APICICR7 0x0000AA08 | ||
76 | #define MIC_X100_SBOX_RGCR 0x00004010 | ||
77 | #define MIC_X100_SBOX_SDBIC0 0x0000CC90 | ||
78 | #define MIC_X100_DOWNLOAD_INFO 2 | ||
79 | #define MIC_X100_FW_SIZE 5 | ||
80 | #define MIC_X100_POSTCODE 0x242c | ||
81 | |||
82 | static const u16 mic_x100_intr_init[] = { | ||
83 | MIC_X100_DOORBELL_IDX_START, | ||
84 | MIC_X100_DMA_IDX_START, | ||
85 | MIC_X100_ERR_IDX_START, | ||
86 | MIC_X100_NUM_DOORBELL, | ||
87 | MIC_X100_NUM_DMA, | ||
88 | MIC_X100_NUM_ERR, | ||
89 | }; | ||
90 | |||
91 | /* Host->Card(bootstrap) Interrupt Vector */ | ||
92 | #define MIC_X100_BSP_INTERRUPT_VECTOR 229 | ||
93 | |||
94 | extern struct mic_hw_ops mic_x100_ops; | ||
95 | extern struct mic_smpt_ops mic_x100_smpt_ops; | ||
96 | extern struct mic_hw_intr_ops mic_x100_intr_ops; | ||
97 | |||
98 | #endif | ||