Commit | Line | Data |
---|---|---|
800f879a AT |
1 | /* |
2 | * Copyright 2010-2017 Intel Corporation. | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License, version 2, | |
6 | * as published by the Free Software Foundation. | |
7 | * | |
8 | * This program is distributed in the hope that it will be useful, | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
11 | * General Public License for more details. | |
12 | * | |
13 | * Disclaimer: The codes contained in these modules may be specific to | |
14 | * the Intel Software Development Platform codenamed Knights Ferry, | |
15 | * and the Intel product codenamed Knights Corner, and are not backward | |
16 | * compatible with other Intel products. Additionally, Intel will NOT | |
17 | * support the codes or instruction set in future products. | |
18 | * | |
19 | * Intel offers no warranty of any kind regarding the code. This code is | |
20 | * licensed on an "AS IS" basis and Intel is not obligated to provide | |
21 | * any support, assistance, installation, training, or other services | |
22 | * of any kind. Intel is also not obligated to provide any updates, | |
23 | * enhancements or extensions. Intel specifically disclaims any warranty | |
24 | * of merchantability, non-infringement, fitness for any particular | |
25 | * purpose, and any other warranty. | |
26 | * | |
27 | * Further, Intel disclaims all liability of any kind, including but | |
28 | * not limited to liability for infringement of any proprietary rights, | |
29 | * relating to the use of the code, even if Intel is notified of the | |
30 | * possibility of such liability. Except as expressly stated in an Intel | |
31 | * license agreement provided with this code and agreed upon with Intel, | |
32 | * no license, express or implied, by estoppel or otherwise, to any | |
33 | * intellectual property rights is granted herein. | |
34 | */ | |
35 | ||
36 | #include <linux/errno.h> | |
37 | #include <linux/hardirq.h> | |
38 | #include <linux/types.h> | |
39 | #include <linux/capability.h> | |
40 | #include <linux/slab.h> | |
41 | #include <linux/string.h> | |
42 | #include <linux/gfp.h> | |
43 | #include <linux/vmalloc.h> | |
44 | #include <asm/io.h> | |
45 | #include <linux/kernel.h> | |
46 | #include <linux/mm_types.h> | |
47 | #include <linux/jiffies.h> | |
48 | #include <linux/timer.h> | |
49 | #include <linux/irqflags.h> | |
50 | #include <linux/time.h> | |
51 | #include <linux/spinlock.h> | |
52 | #include <linux/mutex.h> | |
53 | #include <linux/semaphore.h> | |
54 | #include <linux/kthread.h> | |
55 | #include <linux/sched.h> | |
56 | #include <linux/delay.h> | |
57 | #include <linux/wait.h> | |
58 | #include <asm/bug.h> | |
59 | #include <linux/pci.h> | |
60 | #include <linux/device.h> | |
61 | #include <linux/fs.h> | |
62 | #include <linux/list.h> | |
63 | #include <linux/workqueue.h> | |
64 | #include <linux/interrupt.h> | |
65 | #include <asm/atomic.h> | |
66 | #include <linux/netdevice.h> | |
67 | #include <linux/debugfs.h> | |
68 | #include "micint.h" | |
69 | #include "mic/micveth.h" | |
70 | ||
71 | /* | |
72 | * Retrieves the device context for a particular device | |
73 | */ | |
74 | mic_ctx_t * | |
75 | get_device_context(struct pci_dev *dev) { | |
76 | int i = 0; | |
77 | mic_ctx_t *mic_ctx = NULL; | |
78 | for (i = (mic_data.dd_numdevs -1); i >= 0; i--) { | |
79 | mic_ctx = &mic_data.dd_bi[i]->bi_ctx; | |
80 | if (mic_ctx!= NULL) { | |
81 | //TODO: Is bus number enough to uniquely identify a | |
82 | //pci_dev struct in mic_ctx? | |
83 | if (mic_ctx->bi_pdev->bus->number == | |
84 | dev->bus->number) { | |
85 | ||
86 | //Bus number matches | |
87 | break; | |
88 | } | |
89 | } | |
90 | } | |
91 | return mic_ctx; | |
92 | } | |
93 | ||
94 | /* | |
95 | * Notifier callback with event specifying the actual power management | |
96 | * event to have happened.Our events of Interest right now are: | |
97 | * PM_HIBERNATION_PREPARE and PM_POST_RESTORE | |
98 | */ | |
99 | int | |
100 | micpm_notifier_block(struct notifier_block *nb, unsigned long event, void *dummy) | |
101 | { | |
102 | int i; | |
103 | mic_ctx_t *mic_ctx; | |
104 | switch (event) { | |
105 | case PM_POST_RESTORE: | |
106 | case PM_POST_SUSPEND: | |
107 | case PM_POST_HIBERNATION: | |
108 | pr_debug("%s Calling MIC resume\n", __func__); | |
109 | for(i = 0; i < mic_data.dd_numdevs; i++) { | |
110 | mic_ctx = get_per_dev_ctx(i); | |
111 | if (mic_ctx && mic_ctx->micpm_ctx.resume.wq) { | |
112 | queue_work(mic_ctx->micpm_ctx.resume.wq, | |
113 | &mic_ctx->micpm_ctx.resume.work); | |
114 | } | |
115 | } | |
116 | break; | |
117 | default: | |
118 | pr_debug("%s: Unrecognized event %lu\n", __func__, event); | |
119 | break; | |
120 | } | |
121 | return 0; | |
122 | } | |
123 | ||
124 | /* | |
125 | * Called by the OS when going into suspend. | |
126 | * Puts our device to D3Cold. | |
127 | */ | |
128 | int | |
129 | micpm_suspend(struct device *pdev) | |
130 | { | |
131 | struct pci_dev *pci_dev = to_pci_dev(pdev); | |
132 | mic_ctx_t *mic_ctx = get_device_context(pci_dev); | |
133 | ||
134 | if (!pci_dev) { | |
135 | pr_debug("Not initialized, aborting suspend.\n"); | |
136 | return -ENODEV; | |
137 | } | |
138 | ||
139 | pr_debug("pm_stop_device called for dev: %d:%d:%d\n", pci_dev->bus->number, | |
140 | PCI_SLOT(pci_dev->devfn), PCI_FUNC(pci_dev->devfn)); | |
141 | pm_stop_device(mic_ctx); | |
142 | pci_save_state(pci_dev); | |
143 | pci_disable_device(pci_dev); | |
144 | if (pci_set_power_state(pci_dev, PCI_D3cold)) | |
145 | pr_debug("Not able to set to D3Cold state\n"); | |
146 | pr_debug("Returning from mic_suspend\n"); | |
147 | return 0; | |
148 | } | |
149 | ||
150 | /* | |
151 | * Called by the OS when coming out of suspend. | |
152 | * Puts our device to D0 and starts driver components. | |
153 | */ | |
154 | int | |
155 | micpm_resume(struct device *pdev) | |
156 | { | |
157 | struct pci_dev *pci_dev = to_pci_dev(pdev); | |
158 | if (!pci_dev) { | |
159 | pr_debug("Device not initialized. aborting resume"); | |
160 | return -ENODEV; | |
161 | } | |
162 | ||
163 | pci_set_power_state(pci_dev, PCI_D0); | |
164 | if (pci_enable_device(pci_dev)) { | |
165 | pr_debug("Failed to wake-up device.\n"); | |
166 | return -EIO; | |
167 | } | |
168 | pci_restore_state(pci_dev); | |
169 | pci_set_master(pci_dev); | |
170 | pr_debug("pm_start_device called for dev: %d:%d:%d\n", pci_dev->bus->number, | |
171 | PCI_SLOT(pci_dev->devfn), PCI_FUNC(pci_dev->devfn)); | |
172 | return 0; | |
173 | } | |
174 | ||
175 | int micpm_suspend_noirq(struct device *pdev) { | |
176 | ||
177 | struct pci_dev *pci_dev = to_pci_dev(pdev); | |
178 | mic_ctx_t *mic_ctx; | |
179 | bd_info_t *bd_info; | |
180 | ||
181 | if (!pci_dev) { | |
182 | pr_debug("Device not initialized. aborting suspend"); | |
183 | return -ENODEV; | |
184 | } | |
185 | ||
186 | mic_ctx = get_device_context(pci_dev); | |
187 | if(mic_ctx) { | |
188 | bd_info = mic_ctx->bd_info; | |
189 | /* MSI interrupts do not work on resume. | |
190 | * See http://www.digipedia.pl/usenet/thread/18815/2513/ | |
191 | * for a discussion on this issue. | |
192 | */ | |
193 | if (mic_ctx->msie) { | |
194 | free_irq(bd_info->bi_msix_entries[0].vector, &bd_info->bi_ctx); | |
195 | } | |
196 | } | |
197 | return 0; | |
198 | } | |
199 | ||
200 | int micpm_resume_noirq(struct device *pdev) { | |
201 | ||
202 | struct pci_dev *pci_dev = to_pci_dev(pdev); | |
203 | mic_ctx_t *mic_ctx; | |
204 | bd_info_t *bd_info; | |
205 | int err; | |
206 | ||
207 | if (!pci_dev) { | |
208 | pr_debug("Device not initialized. aborting resume"); | |
209 | return -ENODEV; | |
210 | } | |
211 | mic_ctx = get_device_context(pci_dev); | |
212 | if(mic_ctx) { | |
213 | bd_info = mic_ctx->bd_info; | |
214 | ||
215 | /* MSI interrupts do not work on resume. | |
216 | * See http://www.digipedia.pl/usenet/thread/18815/2513/ | |
217 | * for a discussion on this issue. | |
218 | */ | |
219 | if (mic_ctx->msie) { | |
220 | err = request_irq(bd_info->bi_msix_entries[0].vector, | |
221 | mic_irq_isr, 0, "mic", mic_ctx); | |
222 | if (err) { | |
223 | pr_debug("%s: %d Error inititalizing MSI interrupts\n", | |
224 | __func__, __LINE__); | |
225 | return 0; | |
226 | } | |
227 | } | |
228 | ||
229 | } | |
230 | return 0; | |
231 | } | |
232 |