1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * system.c - a driver for reserving pnp system resources |
4 | * |
5 | * Some code is based on pnpbios_core.c |
6 | * Copyright 2002 Adam Belay <ambx1@neo.rr.com> |
7 | * (c) Copyright 2007 Hewlett-Packard Development Company, L.P. |
8 | * Bjorn Helgaas <bjorn.helgaas@hp.com> |
9 | */ |
10 | |
11 | #include <linux/pnp.h> |
12 | #include <linux/device.h> |
13 | #include <linux/init.h> |
14 | #include <linux/slab.h> |
15 | #include <linux/kernel.h> |
16 | #include <linux/ioport.h> |
17 | |
18 | static const struct pnp_device_id pnp_dev_table[] = { |
19 | /* General ID for reserving resources */ |
20 | {"PNP0c02" , 0}, |
21 | /* memory controller */ |
22 | {"PNP0c01" , 0}, |
23 | {"" , 0} |
24 | }; |
25 | |
26 | static void reserve_range(struct pnp_dev *dev, struct resource *r, int port) |
27 | { |
28 | char *regionid; |
29 | const char *pnpid = dev_name(dev: &dev->dev); |
30 | resource_size_t start = r->start, end = r->end; |
31 | struct resource *res; |
32 | |
33 | regionid = kmalloc(size: 16, GFP_KERNEL); |
34 | if (!regionid) |
35 | return; |
36 | |
37 | snprintf(buf: regionid, size: 16, fmt: "pnp %s" , pnpid); |
38 | if (port) |
39 | res = request_region(start, end - start + 1, regionid); |
40 | else |
41 | res = request_mem_region(start, end - start + 1, regionid); |
42 | if (res) |
43 | res->flags &= ~IORESOURCE_BUSY; |
44 | else |
45 | kfree(objp: regionid); |
46 | |
47 | /* |
48 | * Failures at this point are usually harmless. pci quirks for |
49 | * example do reserve stuff they know about too, so we may well |
50 | * have double reservations. |
51 | */ |
52 | dev_info(&dev->dev, "%pR %s reserved\n" , r, |
53 | res ? "has been" : "could not be" ); |
54 | } |
55 | |
56 | static void reserve_resources_of_dev(struct pnp_dev *dev) |
57 | { |
58 | struct resource *res; |
59 | int i; |
60 | |
61 | for (i = 0; (res = pnp_get_resource(dev, IORESOURCE_IO, num: i)); i++) { |
62 | if (res->flags & IORESOURCE_DISABLED) |
63 | continue; |
64 | if (res->start == 0) |
65 | continue; /* disabled */ |
66 | if (res->start < 0x100) |
67 | /* |
68 | * Below 0x100 is only standard PC hardware |
69 | * (pics, kbd, timer, dma, ...) |
70 | * We should not get resource conflicts there, |
71 | * and the kernel reserves these anyway |
72 | * (see arch/i386/kernel/setup.c). |
73 | * So, do nothing |
74 | */ |
75 | continue; |
76 | if (res->end < res->start) |
77 | continue; /* invalid */ |
78 | |
79 | reserve_range(dev, r: res, port: 1); |
80 | } |
81 | |
82 | for (i = 0; (res = pnp_get_resource(dev, IORESOURCE_MEM, num: i)); i++) { |
83 | if (res->flags & IORESOURCE_DISABLED) |
84 | continue; |
85 | |
86 | reserve_range(dev, r: res, port: 0); |
87 | } |
88 | } |
89 | |
90 | static int system_pnp_probe(struct pnp_dev *dev, |
91 | const struct pnp_device_id *dev_id) |
92 | { |
93 | reserve_resources_of_dev(dev); |
94 | return 0; |
95 | } |
96 | |
97 | static struct pnp_driver system_pnp_driver = { |
98 | .name = "system" , |
99 | .id_table = pnp_dev_table, |
100 | .flags = PNP_DRIVER_RES_DO_NOT_CHANGE, |
101 | .probe = system_pnp_probe, |
102 | }; |
103 | |
104 | static int __init pnp_system_init(void) |
105 | { |
106 | return pnp_register_driver(drv: &system_pnp_driver); |
107 | } |
108 | |
109 | /* |
110 | * Reserve motherboard resources after PCI claim BARs, |
111 | * but before PCI assign resources for uninitialized PCI devices |
112 | */ |
113 | fs_initcall(pnp_system_init); |
114 | |