1c39f472eSBen Skeggs /*
2c39f472eSBen Skeggs * Copyright 2012 Red Hat Inc.
3c39f472eSBen Skeggs *
4c39f472eSBen Skeggs * Permission is hereby granted, free of charge, to any person obtaining a
5c39f472eSBen Skeggs * copy of this software and associated documentation files (the "Software"),
6c39f472eSBen Skeggs * to deal in the Software without restriction, including without limitation
7c39f472eSBen Skeggs * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8c39f472eSBen Skeggs * and/or sell copies of the Software, and to permit persons to whom the
9c39f472eSBen Skeggs * Software is furnished to do so, subject to the following conditions:
10c39f472eSBen Skeggs *
11c39f472eSBen Skeggs * The above copyright notice and this permission notice shall be included in
12c39f472eSBen Skeggs * all copies or substantial portions of the Software.
13c39f472eSBen Skeggs *
14c39f472eSBen Skeggs * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15c39f472eSBen Skeggs * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16c39f472eSBen Skeggs * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17c39f472eSBen Skeggs * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18c39f472eSBen Skeggs * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19c39f472eSBen Skeggs * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20c39f472eSBen Skeggs * OTHER DEALINGS IN THE SOFTWARE.
21c39f472eSBen Skeggs *
22c39f472eSBen Skeggs * Authors: Ben Skeggs
23c39f472eSBen Skeggs */
24c39f472eSBen Skeggs #include "priv.h"
25c39f472eSBen Skeggs
26d8e83994SBen Skeggs #include <subdev/bar.h>
2778b2b4e7SBen Skeggs
28c39f472eSBen Skeggs /******************************************************************************
29c39f472eSBen Skeggs * instmem object base implementation
30c39f472eSBen Skeggs *****************************************************************************/
31d52ddc95SBen Skeggs static void
nvkm_instobj_load(struct nvkm_instobj * iobj)32d52ddc95SBen Skeggs nvkm_instobj_load(struct nvkm_instobj *iobj)
33d52ddc95SBen Skeggs {
34d52ddc95SBen Skeggs struct nvkm_memory *memory = &iobj->memory;
35d52ddc95SBen Skeggs const u64 size = nvkm_memory_size(memory);
36ffd937bbSBen Skeggs void __iomem *map;
37d52ddc95SBen Skeggs int i;
38d52ddc95SBen Skeggs
39ffd937bbSBen Skeggs if (!(map = nvkm_kmap(memory))) {
40d52ddc95SBen Skeggs for (i = 0; i < size; i += 4)
41d52ddc95SBen Skeggs nvkm_wo32(memory, i, iobj->suspend[i / 4]);
42ffd937bbSBen Skeggs } else {
43ffd937bbSBen Skeggs memcpy_toio(map, iobj->suspend, size);
44ffd937bbSBen Skeggs }
45ffd937bbSBen Skeggs nvkm_done(memory);
46ffd937bbSBen Skeggs
4754c70e3aSBen Skeggs kvfree(iobj->suspend);
48d52ddc95SBen Skeggs iobj->suspend = NULL;
49d52ddc95SBen Skeggs }
50d52ddc95SBen Skeggs
51d52ddc95SBen Skeggs static int
nvkm_instobj_save(struct nvkm_instobj * iobj)52d52ddc95SBen Skeggs nvkm_instobj_save(struct nvkm_instobj *iobj)
53d52ddc95SBen Skeggs {
54d52ddc95SBen Skeggs struct nvkm_memory *memory = &iobj->memory;
55d52ddc95SBen Skeggs const u64 size = nvkm_memory_size(memory);
56e9be3c7dSBen Skeggs void __iomem *map;
57d52ddc95SBen Skeggs int i;
58d52ddc95SBen Skeggs
5954c70e3aSBen Skeggs iobj->suspend = kvmalloc(size, GFP_KERNEL);
60d52ddc95SBen Skeggs if (!iobj->suspend)
61d52ddc95SBen Skeggs return -ENOMEM;
62d52ddc95SBen Skeggs
63e9be3c7dSBen Skeggs if (!(map = nvkm_kmap(memory))) {
64d52ddc95SBen Skeggs for (i = 0; i < size; i += 4)
65d52ddc95SBen Skeggs iobj->suspend[i / 4] = nvkm_ro32(memory, i);
66e9be3c7dSBen Skeggs } else {
67e9be3c7dSBen Skeggs memcpy_fromio(iobj->suspend, map, size);
68e9be3c7dSBen Skeggs }
69e9be3c7dSBen Skeggs nvkm_done(memory);
70d52ddc95SBen Skeggs return 0;
71d52ddc95SBen Skeggs }
72d52ddc95SBen Skeggs
7349814f62SBen Skeggs void
nvkm_instobj_dtor(struct nvkm_instmem * imem,struct nvkm_instobj * iobj)7449814f62SBen Skeggs nvkm_instobj_dtor(struct nvkm_instmem *imem, struct nvkm_instobj *iobj)
7549814f62SBen Skeggs {
7649814f62SBen Skeggs spin_lock(&imem->lock);
7749814f62SBen Skeggs list_del(&iobj->head);
7849814f62SBen Skeggs spin_unlock(&imem->lock);
7949814f62SBen Skeggs }
8049814f62SBen Skeggs
8149814f62SBen Skeggs void
nvkm_instobj_ctor(const struct nvkm_memory_func * func,struct nvkm_instmem * imem,struct nvkm_instobj * iobj)8249814f62SBen Skeggs nvkm_instobj_ctor(const struct nvkm_memory_func *func,
8349814f62SBen Skeggs struct nvkm_instmem *imem, struct nvkm_instobj *iobj)
8449814f62SBen Skeggs {
8549814f62SBen Skeggs nvkm_memory_ctor(func, &iobj->memory);
8649814f62SBen Skeggs iobj->suspend = NULL;
8749814f62SBen Skeggs spin_lock(&imem->lock);
8849814f62SBen Skeggs list_add_tail(&iobj->head, &imem->list);
8949814f62SBen Skeggs spin_unlock(&imem->lock);
9049814f62SBen Skeggs }
9149814f62SBen Skeggs
92b7a2bc18SBen Skeggs int
nvkm_instobj_wrap(struct nvkm_device * device,struct nvkm_memory * memory,struct nvkm_memory ** pmemory)93*973b3244SBen Skeggs nvkm_instobj_wrap(struct nvkm_device *device,
94*973b3244SBen Skeggs struct nvkm_memory *memory, struct nvkm_memory **pmemory)
95*973b3244SBen Skeggs {
96*973b3244SBen Skeggs struct nvkm_instmem *imem = device->imem;
97*973b3244SBen Skeggs
98*973b3244SBen Skeggs if (!imem->func->memory_wrap)
99*973b3244SBen Skeggs return -ENOSYS;
100*973b3244SBen Skeggs
101*973b3244SBen Skeggs return imem->func->memory_wrap(imem, memory, pmemory);
102*973b3244SBen Skeggs }
103*973b3244SBen Skeggs
104*973b3244SBen Skeggs int
nvkm_instobj_new(struct nvkm_instmem * imem,u32 size,u32 align,bool zero,struct nvkm_memory ** pmemory)105d8e83994SBen Skeggs nvkm_instobj_new(struct nvkm_instmem *imem, u32 size, u32 align, bool zero,
106d8e83994SBen Skeggs struct nvkm_memory **pmemory)
107d8e83994SBen Skeggs {
108dde59b9cSBen Skeggs struct nvkm_subdev *subdev = &imem->subdev;
109b7a2bc18SBen Skeggs struct nvkm_memory *memory = NULL;
110d8e83994SBen Skeggs u32 offset;
111c39f472eSBen Skeggs int ret;
112c39f472eSBen Skeggs
113b7a2bc18SBen Skeggs ret = imem->func->memory_new(imem, size, align, zero, &memory);
114dde59b9cSBen Skeggs if (ret) {
115dde59b9cSBen Skeggs nvkm_error(subdev, "OOM: %08x %08x %d\n", size, align, ret);
116d8e83994SBen Skeggs goto done;
117dde59b9cSBen Skeggs }
118dde59b9cSBen Skeggs
119dde59b9cSBen Skeggs nvkm_trace(subdev, "new %08x %08x %d: %010llx %010llx\n", size, align,
120dde59b9cSBen Skeggs zero, nvkm_memory_addr(memory), nvkm_memory_size(memory));
121c39f472eSBen Skeggs
122b7a2bc18SBen Skeggs if (!imem->func->zero && zero) {
123d8e83994SBen Skeggs void __iomem *map = nvkm_kmap(memory);
124d8e83994SBen Skeggs if (unlikely(!map)) {
125d8e83994SBen Skeggs for (offset = 0; offset < size; offset += 4)
126d8e83994SBen Skeggs nvkm_wo32(memory, offset, 0x00000000);
127d8e83994SBen Skeggs } else {
128d8e83994SBen Skeggs memset_io(map, 0x00, size);
129d8e83994SBen Skeggs }
130d8e83994SBen Skeggs nvkm_done(memory);
131d8e83994SBen Skeggs }
132d8e83994SBen Skeggs
133d8e83994SBen Skeggs done:
134d8e83994SBen Skeggs if (ret)
135997a8900SBen Skeggs nvkm_memory_unref(&memory);
136d8e83994SBen Skeggs *pmemory = memory;
137d8e83994SBen Skeggs return ret;
138c39f472eSBen Skeggs }
139c39f472eSBen Skeggs
140c39f472eSBen Skeggs /******************************************************************************
141c39f472eSBen Skeggs * instmem subdev base implementation
142c39f472eSBen Skeggs *****************************************************************************/
143c39f472eSBen Skeggs
144b7a2bc18SBen Skeggs u32
nvkm_instmem_rd32(struct nvkm_instmem * imem,u32 addr)145b7a2bc18SBen Skeggs nvkm_instmem_rd32(struct nvkm_instmem *imem, u32 addr)
146c39f472eSBen Skeggs {
147b7a2bc18SBen Skeggs return imem->func->rd32(imem, addr);
148b7a2bc18SBen Skeggs }
149b7a2bc18SBen Skeggs
150b7a2bc18SBen Skeggs void
nvkm_instmem_wr32(struct nvkm_instmem * imem,u32 addr,u32 data)151b7a2bc18SBen Skeggs nvkm_instmem_wr32(struct nvkm_instmem *imem, u32 addr, u32 data)
152b7a2bc18SBen Skeggs {
153b7a2bc18SBen Skeggs return imem->func->wr32(imem, addr, data);
154b7a2bc18SBen Skeggs }
155b7a2bc18SBen Skeggs
156b00b8430SBen Skeggs void
nvkm_instmem_boot(struct nvkm_instmem * imem)157b00b8430SBen Skeggs nvkm_instmem_boot(struct nvkm_instmem *imem)
158b00b8430SBen Skeggs {
159b00b8430SBen Skeggs /* Separate bootstrapped objects from normal list, as we need
160b00b8430SBen Skeggs * to make sure they're accessed with the slowpath on suspend
161b00b8430SBen Skeggs * and resume.
162b00b8430SBen Skeggs */
163b00b8430SBen Skeggs struct nvkm_instobj *iobj, *itmp;
164b00b8430SBen Skeggs spin_lock(&imem->lock);
165b00b8430SBen Skeggs list_for_each_entry_safe(iobj, itmp, &imem->list, head) {
166b00b8430SBen Skeggs list_move_tail(&iobj->head, &imem->boot);
167b00b8430SBen Skeggs }
168b00b8430SBen Skeggs spin_unlock(&imem->lock);
169b00b8430SBen Skeggs }
170b00b8430SBen Skeggs
171b7a2bc18SBen Skeggs static int
nvkm_instmem_fini(struct nvkm_subdev * subdev,bool suspend)172b7a2bc18SBen Skeggs nvkm_instmem_fini(struct nvkm_subdev *subdev, bool suspend)
173b7a2bc18SBen Skeggs {
174b7a2bc18SBen Skeggs struct nvkm_instmem *imem = nvkm_instmem(subdev);
17578b2b4e7SBen Skeggs struct nvkm_instobj *iobj;
176d52ddc95SBen Skeggs
177d52ddc95SBen Skeggs if (suspend) {
178d52ddc95SBen Skeggs list_for_each_entry(iobj, &imem->list, head) {
179d52ddc95SBen Skeggs int ret = nvkm_instobj_save(iobj);
180d52ddc95SBen Skeggs if (ret)
181d52ddc95SBen Skeggs return ret;
182d52ddc95SBen Skeggs }
183b00b8430SBen Skeggs
184e9be3c7dSBen Skeggs nvkm_bar_bar2_fini(subdev->device);
185e9be3c7dSBen Skeggs
186b00b8430SBen Skeggs list_for_each_entry(iobj, &imem->boot, head) {
187b00b8430SBen Skeggs int ret = nvkm_instobj_save(iobj);
188b00b8430SBen Skeggs if (ret)
189b00b8430SBen Skeggs return ret;
190b00b8430SBen Skeggs }
191d52ddc95SBen Skeggs }
192c39f472eSBen Skeggs
193b7a2bc18SBen Skeggs if (imem->func->fini)
194b7a2bc18SBen Skeggs imem->func->fini(imem);
195b7a2bc18SBen Skeggs
196d52ddc95SBen Skeggs return 0;
19795cf469cSBen Skeggs }
198d52ddc95SBen Skeggs
199d52ddc95SBen Skeggs static int
nvkm_instmem_init(struct nvkm_subdev * subdev)200d52ddc95SBen Skeggs nvkm_instmem_init(struct nvkm_subdev *subdev)
201d52ddc95SBen Skeggs {
202d52ddc95SBen Skeggs struct nvkm_instmem *imem = nvkm_instmem(subdev);
203d52ddc95SBen Skeggs struct nvkm_instobj *iobj;
204d52ddc95SBen Skeggs
205b00b8430SBen Skeggs list_for_each_entry(iobj, &imem->boot, head) {
206b00b8430SBen Skeggs if (iobj->suspend)
207b00b8430SBen Skeggs nvkm_instobj_load(iobj);
208b00b8430SBen Skeggs }
209b00b8430SBen Skeggs
210ffd937bbSBen Skeggs nvkm_bar_bar2_init(subdev->device);
211ffd937bbSBen Skeggs
212d52ddc95SBen Skeggs list_for_each_entry(iobj, &imem->list, head) {
213d52ddc95SBen Skeggs if (iobj->suspend)
214d52ddc95SBen Skeggs nvkm_instobj_load(iobj);
215c39f472eSBen Skeggs }
216c39f472eSBen Skeggs
217b7a2bc18SBen Skeggs return 0;
218c39f472eSBen Skeggs }
219c39f472eSBen Skeggs
220b7a2bc18SBen Skeggs static int
nvkm_instmem_oneinit(struct nvkm_subdev * subdev)221b7a2bc18SBen Skeggs nvkm_instmem_oneinit(struct nvkm_subdev *subdev)
222c39f472eSBen Skeggs {
223b7a2bc18SBen Skeggs struct nvkm_instmem *imem = nvkm_instmem(subdev);
224b7a2bc18SBen Skeggs if (imem->func->oneinit)
225b7a2bc18SBen Skeggs return imem->func->oneinit(imem);
226b7a2bc18SBen Skeggs return 0;
227b7a2bc18SBen Skeggs }
228c39f472eSBen Skeggs
229b7a2bc18SBen Skeggs static void *
nvkm_instmem_dtor(struct nvkm_subdev * subdev)230b7a2bc18SBen Skeggs nvkm_instmem_dtor(struct nvkm_subdev *subdev)
231c39f472eSBen Skeggs {
232b7a2bc18SBen Skeggs struct nvkm_instmem *imem = nvkm_instmem(subdev);
233e5bf9a5cSBen Skeggs void *data = imem;
234b7a2bc18SBen Skeggs if (imem->func->dtor)
235e5bf9a5cSBen Skeggs data = imem->func->dtor(imem);
236e5bf9a5cSBen Skeggs mutex_destroy(&imem->mutex);
237e5bf9a5cSBen Skeggs return data;
238b7a2bc18SBen Skeggs }
239c39f472eSBen Skeggs
240b7a2bc18SBen Skeggs static const struct nvkm_subdev_func
241b7a2bc18SBen Skeggs nvkm_instmem = {
242b7a2bc18SBen Skeggs .dtor = nvkm_instmem_dtor,
243b7a2bc18SBen Skeggs .oneinit = nvkm_instmem_oneinit,
244b7a2bc18SBen Skeggs .init = nvkm_instmem_init,
245b7a2bc18SBen Skeggs .fini = nvkm_instmem_fini,
246b7a2bc18SBen Skeggs };
247c39f472eSBen Skeggs
248b7a2bc18SBen Skeggs void
nvkm_instmem_ctor(const struct nvkm_instmem_func * func,struct nvkm_device * device,enum nvkm_subdev_type type,int inst,struct nvkm_instmem * imem)249d9691a22SBen Skeggs nvkm_instmem_ctor(const struct nvkm_instmem_func *func, struct nvkm_device *device,
250d9691a22SBen Skeggs enum nvkm_subdev_type type, int inst, struct nvkm_instmem *imem)
251b7a2bc18SBen Skeggs {
252d9691a22SBen Skeggs nvkm_subdev_ctor(&nvkm_instmem, device, type, inst, &imem->subdev);
253b7a2bc18SBen Skeggs imem->func = func;
25495095032SBen Skeggs spin_lock_init(&imem->lock);
255c39f472eSBen Skeggs INIT_LIST_HEAD(&imem->list);
256b00b8430SBen Skeggs INIT_LIST_HEAD(&imem->boot);
257e5bf9a5cSBen Skeggs mutex_init(&imem->mutex);
258c39f472eSBen Skeggs }
259