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  */
23c39f472eSBen Skeggs #include "priv.h"
24c39f472eSBen Skeggs 
2526c9e8efSBen Skeggs #include <core/pci.h>
2626c9e8efSBen Skeggs 
27c39f472eSBen Skeggs struct priv {
28c39f472eSBen Skeggs 	struct pci_dev *pdev;
29c39f472eSBen Skeggs 	void __iomem *rom;
30c39f472eSBen Skeggs 	size_t size;
31c39f472eSBen Skeggs };
32c39f472eSBen Skeggs 
33c39f472eSBen Skeggs static u32
pcirom_read(void * data,u32 offset,u32 length,struct nvkm_bios * bios)34d390b480SBen Skeggs pcirom_read(void *data, u32 offset, u32 length, struct nvkm_bios *bios)
35c39f472eSBen Skeggs {
36c39f472eSBen Skeggs 	struct priv *priv = data;
37c39f472eSBen Skeggs 	if (offset + length <= priv->size) {
38c39f472eSBen Skeggs 		memcpy_fromio(bios->data + offset, priv->rom + offset, length);
39c39f472eSBen Skeggs 		return length;
40c39f472eSBen Skeggs 	}
41c39f472eSBen Skeggs 	return 0;
42c39f472eSBen Skeggs }
43c39f472eSBen Skeggs 
44c39f472eSBen Skeggs static void
pcirom_fini(void * data)45c39f472eSBen Skeggs pcirom_fini(void *data)
46c39f472eSBen Skeggs {
47c39f472eSBen Skeggs 	struct priv *priv = data;
48c39f472eSBen Skeggs 	pci_unmap_rom(priv->pdev, priv->rom);
49c39f472eSBen Skeggs 	pci_disable_rom(priv->pdev);
50c39f472eSBen Skeggs 	kfree(priv);
51c39f472eSBen Skeggs }
52c39f472eSBen Skeggs 
53c39f472eSBen Skeggs static void *
pcirom_init(struct nvkm_bios * bios,const char * name)54d390b480SBen Skeggs pcirom_init(struct nvkm_bios *bios, const char *name)
55c39f472eSBen Skeggs {
5626c9e8efSBen Skeggs 	struct nvkm_device *device = bios->subdev.device;
57c39f472eSBen Skeggs 	struct priv *priv = NULL;
5826c9e8efSBen Skeggs 	struct pci_dev *pdev;
59c39f472eSBen Skeggs 	int ret;
60c39f472eSBen Skeggs 
6126c9e8efSBen Skeggs 	if (device->func->pci)
6226c9e8efSBen Skeggs 		pdev = device->func->pci(device)->pdev;
6326c9e8efSBen Skeggs 	else
6426c9e8efSBen Skeggs 		return ERR_PTR(-ENODEV);
6526c9e8efSBen Skeggs 
66c39f472eSBen Skeggs 	if (!(ret = pci_enable_rom(pdev))) {
67c39f472eSBen Skeggs 		if (ret = -ENOMEM,
68c39f472eSBen Skeggs 		    (priv = kmalloc(sizeof(*priv), GFP_KERNEL))) {
69c39f472eSBen Skeggs 			if (ret = -EFAULT,
70c39f472eSBen Skeggs 			    (priv->rom = pci_map_rom(pdev, &priv->size))) {
71c39f472eSBen Skeggs 				priv->pdev = pdev;
72c39f472eSBen Skeggs 				return priv;
73c39f472eSBen Skeggs 			}
74c39f472eSBen Skeggs 			kfree(priv);
75c39f472eSBen Skeggs 		}
76c39f472eSBen Skeggs 		pci_disable_rom(pdev);
77c39f472eSBen Skeggs 	}
78c39f472eSBen Skeggs 
79c39f472eSBen Skeggs 	return ERR_PTR(ret);
80c39f472eSBen Skeggs }
81c39f472eSBen Skeggs 
82c39f472eSBen Skeggs const struct nvbios_source
83c39f472eSBen Skeggs nvbios_pcirom = {
84c39f472eSBen Skeggs 	.name = "PCIROM",
85c39f472eSBen Skeggs 	.init = pcirom_init,
86c39f472eSBen Skeggs 	.fini = pcirom_fini,
87c39f472eSBen Skeggs 	.read = pcirom_read,
88c39f472eSBen Skeggs 	.rw = true,
89c39f472eSBen Skeggs };
90c39f472eSBen Skeggs 
91c39f472eSBen Skeggs static void *
platform_init(struct nvkm_bios * bios,const char * name)92d390b480SBen Skeggs platform_init(struct nvkm_bios *bios, const char *name)
93c39f472eSBen Skeggs {
9426c9e8efSBen Skeggs 	struct nvkm_device *device = bios->subdev.device;
9526c9e8efSBen Skeggs 	struct pci_dev *pdev;
96c39f472eSBen Skeggs 	struct priv *priv;
97c39f472eSBen Skeggs 	int ret = -ENOMEM;
98c39f472eSBen Skeggs 
9926c9e8efSBen Skeggs 	if (device->func->pci)
10026c9e8efSBen Skeggs 		pdev = device->func->pci(device)->pdev;
10126c9e8efSBen Skeggs 	else
10226c9e8efSBen Skeggs 		return ERR_PTR(-ENODEV);
10326c9e8efSBen Skeggs 
10472e0ef0eSMikel Rychliski 	if (!pdev->rom || pdev->romlen == 0)
10572e0ef0eSMikel Rychliski 		return ERR_PTR(-ENODEV);
10672e0ef0eSMikel Rychliski 
107c39f472eSBen Skeggs 	if ((priv = kmalloc(sizeof(*priv), GFP_KERNEL))) {
10872e0ef0eSMikel Rychliski 		priv->size = pdev->romlen;
109c39f472eSBen Skeggs 		if (ret = -ENODEV,
11072e0ef0eSMikel Rychliski 		    (priv->rom = ioremap(pdev->rom, pdev->romlen)))
111c39f472eSBen Skeggs 			return priv;
112c39f472eSBen Skeggs 		kfree(priv);
113c39f472eSBen Skeggs 	}
114c39f472eSBen Skeggs 
115c39f472eSBen Skeggs 	return ERR_PTR(ret);
116c39f472eSBen Skeggs }
117c39f472eSBen Skeggs 
11872e0ef0eSMikel Rychliski static void
platform_fini(void * data)11972e0ef0eSMikel Rychliski platform_fini(void *data)
12072e0ef0eSMikel Rychliski {
12172e0ef0eSMikel Rychliski 	struct priv *priv = data;
12272e0ef0eSMikel Rychliski 
12372e0ef0eSMikel Rychliski 	iounmap(priv->rom);
12472e0ef0eSMikel Rychliski 	kfree(priv);
12572e0ef0eSMikel Rychliski }
12672e0ef0eSMikel Rychliski 
127c39f472eSBen Skeggs const struct nvbios_source
128c39f472eSBen Skeggs nvbios_platform = {
129c39f472eSBen Skeggs 	.name = "PLATFORM",
130c39f472eSBen Skeggs 	.init = platform_init,
13172e0ef0eSMikel Rychliski 	.fini = platform_fini,
132c39f472eSBen Skeggs 	.read = pcirom_read,
133c39f472eSBen Skeggs 	.rw = true,
134c39f472eSBen Skeggs };
135