Updated `README.md` with instructions for building/using the kernel module.
[xeon-phi-kernel-module] / host / linpm.c
CommitLineData
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 */
74mic_ctx_t *
75get_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 */
99int
100micpm_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 }
121return 0;
122}
123
124/*
125 * Called by the OS when going into suspend.
126 * Puts our device to D3Cold.
127 */
128int
129micpm_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 */
154int
155micpm_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
175int 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
200int 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