18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * system.c - a driver for reserving pnp system resources 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Some code is based on pnpbios_core.c 68c2ecf20Sopenharmony_ci * Copyright 2002 Adam Belay <ambx1@neo.rr.com> 78c2ecf20Sopenharmony_ci * (c) Copyright 2007 Hewlett-Packard Development Company, L.P. 88c2ecf20Sopenharmony_ci * Bjorn Helgaas <bjorn.helgaas@hp.com> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/pnp.h> 128c2ecf20Sopenharmony_ci#include <linux/device.h> 138c2ecf20Sopenharmony_ci#include <linux/init.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci#include <linux/kernel.h> 168c2ecf20Sopenharmony_ci#include <linux/ioport.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistatic const struct pnp_device_id pnp_dev_table[] = { 198c2ecf20Sopenharmony_ci /* General ID for reserving resources */ 208c2ecf20Sopenharmony_ci {"PNP0c02", 0}, 218c2ecf20Sopenharmony_ci /* memory controller */ 228c2ecf20Sopenharmony_ci {"PNP0c01", 0}, 238c2ecf20Sopenharmony_ci {"", 0} 248c2ecf20Sopenharmony_ci}; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic void reserve_range(struct pnp_dev *dev, struct resource *r, int port) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci char *regionid; 298c2ecf20Sopenharmony_ci const char *pnpid = dev_name(&dev->dev); 308c2ecf20Sopenharmony_ci resource_size_t start = r->start, end = r->end; 318c2ecf20Sopenharmony_ci struct resource *res; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci regionid = kmalloc(16, GFP_KERNEL); 348c2ecf20Sopenharmony_ci if (!regionid) 358c2ecf20Sopenharmony_ci return; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci snprintf(regionid, 16, "pnp %s", pnpid); 388c2ecf20Sopenharmony_ci if (port) 398c2ecf20Sopenharmony_ci res = request_region(start, end - start + 1, regionid); 408c2ecf20Sopenharmony_ci else 418c2ecf20Sopenharmony_ci res = request_mem_region(start, end - start + 1, regionid); 428c2ecf20Sopenharmony_ci if (res) 438c2ecf20Sopenharmony_ci res->flags &= ~IORESOURCE_BUSY; 448c2ecf20Sopenharmony_ci else 458c2ecf20Sopenharmony_ci kfree(regionid); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci /* 488c2ecf20Sopenharmony_ci * Failures at this point are usually harmless. pci quirks for 498c2ecf20Sopenharmony_ci * example do reserve stuff they know about too, so we may well 508c2ecf20Sopenharmony_ci * have double reservations. 518c2ecf20Sopenharmony_ci */ 528c2ecf20Sopenharmony_ci dev_info(&dev->dev, "%pR %s reserved\n", r, 538c2ecf20Sopenharmony_ci res ? "has been" : "could not be"); 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic void reserve_resources_of_dev(struct pnp_dev *dev) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci struct resource *res; 598c2ecf20Sopenharmony_ci int i; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci for (i = 0; (res = pnp_get_resource(dev, IORESOURCE_IO, i)); i++) { 628c2ecf20Sopenharmony_ci if (res->flags & IORESOURCE_DISABLED) 638c2ecf20Sopenharmony_ci continue; 648c2ecf20Sopenharmony_ci if (res->start == 0) 658c2ecf20Sopenharmony_ci continue; /* disabled */ 668c2ecf20Sopenharmony_ci if (res->start < 0x100) 678c2ecf20Sopenharmony_ci /* 688c2ecf20Sopenharmony_ci * Below 0x100 is only standard PC hardware 698c2ecf20Sopenharmony_ci * (pics, kbd, timer, dma, ...) 708c2ecf20Sopenharmony_ci * We should not get resource conflicts there, 718c2ecf20Sopenharmony_ci * and the kernel reserves these anyway 728c2ecf20Sopenharmony_ci * (see arch/i386/kernel/setup.c). 738c2ecf20Sopenharmony_ci * So, do nothing 748c2ecf20Sopenharmony_ci */ 758c2ecf20Sopenharmony_ci continue; 768c2ecf20Sopenharmony_ci if (res->end < res->start) 778c2ecf20Sopenharmony_ci continue; /* invalid */ 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci reserve_range(dev, res, 1); 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci for (i = 0; (res = pnp_get_resource(dev, IORESOURCE_MEM, i)); i++) { 838c2ecf20Sopenharmony_ci if (res->flags & IORESOURCE_DISABLED) 848c2ecf20Sopenharmony_ci continue; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci reserve_range(dev, res, 0); 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic int system_pnp_probe(struct pnp_dev *dev, 918c2ecf20Sopenharmony_ci const struct pnp_device_id *dev_id) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci reserve_resources_of_dev(dev); 948c2ecf20Sopenharmony_ci return 0; 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic struct pnp_driver system_pnp_driver = { 988c2ecf20Sopenharmony_ci .name = "system", 998c2ecf20Sopenharmony_ci .id_table = pnp_dev_table, 1008c2ecf20Sopenharmony_ci .flags = PNP_DRIVER_RES_DO_NOT_CHANGE, 1018c2ecf20Sopenharmony_ci .probe = system_pnp_probe, 1028c2ecf20Sopenharmony_ci}; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic int __init pnp_system_init(void) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci return pnp_register_driver(&system_pnp_driver); 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci/** 1108c2ecf20Sopenharmony_ci * Reserve motherboard resources after PCI claim BARs, 1118c2ecf20Sopenharmony_ci * but before PCI assign resources for uninitialized PCI devices 1128c2ecf20Sopenharmony_ci */ 1138c2ecf20Sopenharmony_cifs_initcall(pnp_system_init); 114