162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * resource.c - Contains functions for registering and analyzing resource information 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * based on isapnp.c resource management (c) Jaroslav Kysela <perex@perex.cz> 662306a36Sopenharmony_ci * Copyright 2003 Adam Belay <ambx1@neo.rr.com> 762306a36Sopenharmony_ci * Copyright (C) 2008 Hewlett-Packard Development Company, L.P. 862306a36Sopenharmony_ci * Bjorn Helgaas <bjorn.helgaas@hp.com> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci#include <linux/errno.h> 1462306a36Sopenharmony_ci#include <linux/interrupt.h> 1562306a36Sopenharmony_ci#include <linux/kernel.h> 1662306a36Sopenharmony_ci#include <asm/io.h> 1762306a36Sopenharmony_ci#include <asm/dma.h> 1862306a36Sopenharmony_ci#include <asm/irq.h> 1962306a36Sopenharmony_ci#include <linux/pci.h> 2062306a36Sopenharmony_ci#include <linux/libata.h> 2162306a36Sopenharmony_ci#include <linux/ioport.h> 2262306a36Sopenharmony_ci#include <linux/init.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include <linux/pnp.h> 2562306a36Sopenharmony_ci#include "base.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic int pnp_reserve_irq[16] = {[0 ... 15] = -1 }; /* reserve (don't use) some IRQ */ 2862306a36Sopenharmony_cistatic int pnp_reserve_dma[8] = {[0 ... 7] = -1 }; /* reserve (don't use) some DMA */ 2962306a36Sopenharmony_cistatic int pnp_reserve_io[16] = {[0 ... 15] = -1 }; /* reserve (don't use) some I/O region */ 3062306a36Sopenharmony_cistatic int pnp_reserve_mem[16] = {[0 ... 15] = -1 }; /* reserve (don't use) some memory region */ 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* 3362306a36Sopenharmony_ci * option registration 3462306a36Sopenharmony_ci */ 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic struct pnp_option *pnp_build_option(struct pnp_dev *dev, unsigned long type, 3762306a36Sopenharmony_ci unsigned int option_flags) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci struct pnp_option *option; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci option = kzalloc(sizeof(struct pnp_option), GFP_KERNEL); 4262306a36Sopenharmony_ci if (!option) 4362306a36Sopenharmony_ci return NULL; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci option->flags = option_flags; 4662306a36Sopenharmony_ci option->type = type; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci list_add_tail(&option->list, &dev->options); 4962306a36Sopenharmony_ci return option; 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ciint pnp_register_irq_resource(struct pnp_dev *dev, unsigned int option_flags, 5362306a36Sopenharmony_ci pnp_irq_mask_t *map, unsigned char flags) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci struct pnp_option *option; 5662306a36Sopenharmony_ci struct pnp_irq *irq; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci option = pnp_build_option(dev, IORESOURCE_IRQ, option_flags); 5962306a36Sopenharmony_ci if (!option) 6062306a36Sopenharmony_ci return -ENOMEM; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci irq = &option->u.irq; 6362306a36Sopenharmony_ci irq->map = *map; 6462306a36Sopenharmony_ci irq->flags = flags; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci#ifdef CONFIG_PCI 6762306a36Sopenharmony_ci { 6862306a36Sopenharmony_ci int i; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci for (i = 0; i < 16; i++) 7162306a36Sopenharmony_ci if (test_bit(i, irq->map.bits)) 7262306a36Sopenharmony_ci pcibios_penalize_isa_irq(i, 0); 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci#endif 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci dbg_pnp_show_option(dev, option); 7762306a36Sopenharmony_ci return 0; 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ciint pnp_register_dma_resource(struct pnp_dev *dev, unsigned int option_flags, 8162306a36Sopenharmony_ci unsigned char map, unsigned char flags) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci struct pnp_option *option; 8462306a36Sopenharmony_ci struct pnp_dma *dma; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci option = pnp_build_option(dev, IORESOURCE_DMA, option_flags); 8762306a36Sopenharmony_ci if (!option) 8862306a36Sopenharmony_ci return -ENOMEM; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci dma = &option->u.dma; 9162306a36Sopenharmony_ci dma->map = map; 9262306a36Sopenharmony_ci dma->flags = flags; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci dbg_pnp_show_option(dev, option); 9562306a36Sopenharmony_ci return 0; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ciint pnp_register_port_resource(struct pnp_dev *dev, unsigned int option_flags, 9962306a36Sopenharmony_ci resource_size_t min, resource_size_t max, 10062306a36Sopenharmony_ci resource_size_t align, resource_size_t size, 10162306a36Sopenharmony_ci unsigned char flags) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci struct pnp_option *option; 10462306a36Sopenharmony_ci struct pnp_port *port; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci option = pnp_build_option(dev, IORESOURCE_IO, option_flags); 10762306a36Sopenharmony_ci if (!option) 10862306a36Sopenharmony_ci return -ENOMEM; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci port = &option->u.port; 11162306a36Sopenharmony_ci port->min = min; 11262306a36Sopenharmony_ci port->max = max; 11362306a36Sopenharmony_ci port->align = align; 11462306a36Sopenharmony_ci port->size = size; 11562306a36Sopenharmony_ci port->flags = flags; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci dbg_pnp_show_option(dev, option); 11862306a36Sopenharmony_ci return 0; 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ciint pnp_register_mem_resource(struct pnp_dev *dev, unsigned int option_flags, 12262306a36Sopenharmony_ci resource_size_t min, resource_size_t max, 12362306a36Sopenharmony_ci resource_size_t align, resource_size_t size, 12462306a36Sopenharmony_ci unsigned char flags) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci struct pnp_option *option; 12762306a36Sopenharmony_ci struct pnp_mem *mem; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci option = pnp_build_option(dev, IORESOURCE_MEM, option_flags); 13062306a36Sopenharmony_ci if (!option) 13162306a36Sopenharmony_ci return -ENOMEM; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci mem = &option->u.mem; 13462306a36Sopenharmony_ci mem->min = min; 13562306a36Sopenharmony_ci mem->max = max; 13662306a36Sopenharmony_ci mem->align = align; 13762306a36Sopenharmony_ci mem->size = size; 13862306a36Sopenharmony_ci mem->flags = flags; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci dbg_pnp_show_option(dev, option); 14162306a36Sopenharmony_ci return 0; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_civoid pnp_free_options(struct pnp_dev *dev) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci struct pnp_option *option, *tmp; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci list_for_each_entry_safe(option, tmp, &dev->options, list) { 14962306a36Sopenharmony_ci list_del(&option->list); 15062306a36Sopenharmony_ci kfree(option); 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci/* 15562306a36Sopenharmony_ci * resource validity checking 15662306a36Sopenharmony_ci */ 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci#define length(start, end) (*(end) - *(start) + 1) 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci/* Two ranges conflict if one doesn't end before the other starts */ 16162306a36Sopenharmony_ci#define ranged_conflict(starta, enda, startb, endb) \ 16262306a36Sopenharmony_ci !((*(enda) < *(startb)) || (*(endb) < *(starta))) 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci#define cannot_compare(flags) \ 16562306a36Sopenharmony_ci((flags) & IORESOURCE_DISABLED) 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ciint pnp_check_port(struct pnp_dev *dev, struct resource *res) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci int i; 17062306a36Sopenharmony_ci struct pnp_dev *tdev; 17162306a36Sopenharmony_ci struct resource *tres; 17262306a36Sopenharmony_ci resource_size_t *port, *end, *tport, *tend; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci port = &res->start; 17562306a36Sopenharmony_ci end = &res->end; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci /* if the resource doesn't exist, don't complain about it */ 17862306a36Sopenharmony_ci if (cannot_compare(res->flags)) 17962306a36Sopenharmony_ci return 1; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci /* check if the resource is already in use, skip if the 18262306a36Sopenharmony_ci * device is active because it itself may be in use */ 18362306a36Sopenharmony_ci if (!dev->active) { 18462306a36Sopenharmony_ci if (!request_region(*port, length(port, end), "pnp")) 18562306a36Sopenharmony_ci return 0; 18662306a36Sopenharmony_ci release_region(*port, length(port, end)); 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci /* check if the resource is reserved */ 19062306a36Sopenharmony_ci for (i = 0; i < 8; i++) { 19162306a36Sopenharmony_ci int rport = pnp_reserve_io[i << 1]; 19262306a36Sopenharmony_ci int rend = pnp_reserve_io[(i << 1) + 1] + rport - 1; 19362306a36Sopenharmony_ci if (ranged_conflict(port, end, &rport, &rend)) 19462306a36Sopenharmony_ci return 0; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci /* check for internal conflicts */ 19862306a36Sopenharmony_ci for (i = 0; (tres = pnp_get_resource(dev, IORESOURCE_IO, i)); i++) { 19962306a36Sopenharmony_ci if (tres != res && tres->flags & IORESOURCE_IO) { 20062306a36Sopenharmony_ci tport = &tres->start; 20162306a36Sopenharmony_ci tend = &tres->end; 20262306a36Sopenharmony_ci if (ranged_conflict(port, end, tport, tend)) 20362306a36Sopenharmony_ci return 0; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci /* check for conflicts with other pnp devices */ 20862306a36Sopenharmony_ci pnp_for_each_dev(tdev) { 20962306a36Sopenharmony_ci if (tdev == dev) 21062306a36Sopenharmony_ci continue; 21162306a36Sopenharmony_ci for (i = 0; 21262306a36Sopenharmony_ci (tres = pnp_get_resource(tdev, IORESOURCE_IO, i)); 21362306a36Sopenharmony_ci i++) { 21462306a36Sopenharmony_ci if (tres->flags & IORESOURCE_IO) { 21562306a36Sopenharmony_ci if (cannot_compare(tres->flags)) 21662306a36Sopenharmony_ci continue; 21762306a36Sopenharmony_ci if (tres->flags & IORESOURCE_WINDOW) 21862306a36Sopenharmony_ci continue; 21962306a36Sopenharmony_ci tport = &tres->start; 22062306a36Sopenharmony_ci tend = &tres->end; 22162306a36Sopenharmony_ci if (ranged_conflict(port, end, tport, tend)) 22262306a36Sopenharmony_ci return 0; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci return 1; 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ciint pnp_check_mem(struct pnp_dev *dev, struct resource *res) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci int i; 23362306a36Sopenharmony_ci struct pnp_dev *tdev; 23462306a36Sopenharmony_ci struct resource *tres; 23562306a36Sopenharmony_ci resource_size_t *addr, *end, *taddr, *tend; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci addr = &res->start; 23862306a36Sopenharmony_ci end = &res->end; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci /* if the resource doesn't exist, don't complain about it */ 24162306a36Sopenharmony_ci if (cannot_compare(res->flags)) 24262306a36Sopenharmony_ci return 1; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci /* check if the resource is already in use, skip if the 24562306a36Sopenharmony_ci * device is active because it itself may be in use */ 24662306a36Sopenharmony_ci if (!dev->active) { 24762306a36Sopenharmony_ci if (!request_mem_region(*addr, length(addr, end), "pnp")) 24862306a36Sopenharmony_ci return 0; 24962306a36Sopenharmony_ci release_mem_region(*addr, length(addr, end)); 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci /* check if the resource is reserved */ 25362306a36Sopenharmony_ci for (i = 0; i < 8; i++) { 25462306a36Sopenharmony_ci int raddr = pnp_reserve_mem[i << 1]; 25562306a36Sopenharmony_ci int rend = pnp_reserve_mem[(i << 1) + 1] + raddr - 1; 25662306a36Sopenharmony_ci if (ranged_conflict(addr, end, &raddr, &rend)) 25762306a36Sopenharmony_ci return 0; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* check for internal conflicts */ 26162306a36Sopenharmony_ci for (i = 0; (tres = pnp_get_resource(dev, IORESOURCE_MEM, i)); i++) { 26262306a36Sopenharmony_ci if (tres != res && tres->flags & IORESOURCE_MEM) { 26362306a36Sopenharmony_ci taddr = &tres->start; 26462306a36Sopenharmony_ci tend = &tres->end; 26562306a36Sopenharmony_ci if (ranged_conflict(addr, end, taddr, tend)) 26662306a36Sopenharmony_ci return 0; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci /* check for conflicts with other pnp devices */ 27162306a36Sopenharmony_ci pnp_for_each_dev(tdev) { 27262306a36Sopenharmony_ci if (tdev == dev) 27362306a36Sopenharmony_ci continue; 27462306a36Sopenharmony_ci for (i = 0; 27562306a36Sopenharmony_ci (tres = pnp_get_resource(tdev, IORESOURCE_MEM, i)); 27662306a36Sopenharmony_ci i++) { 27762306a36Sopenharmony_ci if (tres->flags & IORESOURCE_MEM) { 27862306a36Sopenharmony_ci if (cannot_compare(tres->flags)) 27962306a36Sopenharmony_ci continue; 28062306a36Sopenharmony_ci if (tres->flags & IORESOURCE_WINDOW) 28162306a36Sopenharmony_ci continue; 28262306a36Sopenharmony_ci taddr = &tres->start; 28362306a36Sopenharmony_ci tend = &tres->end; 28462306a36Sopenharmony_ci if (ranged_conflict(addr, end, taddr, tend)) 28562306a36Sopenharmony_ci return 0; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci return 1; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic irqreturn_t pnp_test_handler(int irq, void *dev_id) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci return IRQ_HANDLED; 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci#ifdef CONFIG_PCI 29962306a36Sopenharmony_cistatic int pci_dev_uses_irq(struct pnp_dev *pnp, struct pci_dev *pci, 30062306a36Sopenharmony_ci unsigned int irq) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci u32 class; 30362306a36Sopenharmony_ci u8 progif; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if (pci->irq == irq) { 30662306a36Sopenharmony_ci pnp_dbg(&pnp->dev, " device %s using irq %d\n", 30762306a36Sopenharmony_ci pci_name(pci), irq); 30862306a36Sopenharmony_ci return 1; 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci /* 31262306a36Sopenharmony_ci * See pci_setup_device() and ata_pci_sff_activate_host() for 31362306a36Sopenharmony_ci * similar IDE legacy detection. 31462306a36Sopenharmony_ci */ 31562306a36Sopenharmony_ci pci_read_config_dword(pci, PCI_CLASS_REVISION, &class); 31662306a36Sopenharmony_ci class >>= 8; /* discard revision ID */ 31762306a36Sopenharmony_ci progif = class & 0xff; 31862306a36Sopenharmony_ci class >>= 8; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci if (class == PCI_CLASS_STORAGE_IDE) { 32162306a36Sopenharmony_ci /* 32262306a36Sopenharmony_ci * Unless both channels are native-PCI mode only, 32362306a36Sopenharmony_ci * treat the compatibility IRQs as busy. 32462306a36Sopenharmony_ci */ 32562306a36Sopenharmony_ci if ((progif & 0x5) != 0x5) 32662306a36Sopenharmony_ci if (ATA_PRIMARY_IRQ(pci) == irq || 32762306a36Sopenharmony_ci ATA_SECONDARY_IRQ(pci) == irq) { 32862306a36Sopenharmony_ci pnp_dbg(&pnp->dev, " legacy IDE device %s " 32962306a36Sopenharmony_ci "using irq %d\n", pci_name(pci), irq); 33062306a36Sopenharmony_ci return 1; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci return 0; 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci#endif 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_cistatic int pci_uses_irq(struct pnp_dev *pnp, unsigned int irq) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci#ifdef CONFIG_PCI 34162306a36Sopenharmony_ci struct pci_dev *pci = NULL; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci for_each_pci_dev(pci) { 34462306a36Sopenharmony_ci if (pci_dev_uses_irq(pnp, pci, irq)) { 34562306a36Sopenharmony_ci pci_dev_put(pci); 34662306a36Sopenharmony_ci return 1; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci#endif 35062306a36Sopenharmony_ci return 0; 35162306a36Sopenharmony_ci} 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ciint pnp_check_irq(struct pnp_dev *dev, struct resource *res) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci int i; 35662306a36Sopenharmony_ci struct pnp_dev *tdev; 35762306a36Sopenharmony_ci struct resource *tres; 35862306a36Sopenharmony_ci resource_size_t *irq; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci irq = &res->start; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci /* if the resource doesn't exist, don't complain about it */ 36362306a36Sopenharmony_ci if (cannot_compare(res->flags)) 36462306a36Sopenharmony_ci return 1; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci /* check if the resource is valid */ 36762306a36Sopenharmony_ci if (*irq > 15) 36862306a36Sopenharmony_ci return 0; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci /* check if the resource is reserved */ 37162306a36Sopenharmony_ci for (i = 0; i < 16; i++) { 37262306a36Sopenharmony_ci if (pnp_reserve_irq[i] == *irq) 37362306a36Sopenharmony_ci return 0; 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci /* check for internal conflicts */ 37762306a36Sopenharmony_ci for (i = 0; (tres = pnp_get_resource(dev, IORESOURCE_IRQ, i)); i++) { 37862306a36Sopenharmony_ci if (tres != res && tres->flags & IORESOURCE_IRQ) { 37962306a36Sopenharmony_ci if (tres->start == *irq) 38062306a36Sopenharmony_ci return 0; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci /* check if the resource is being used by a pci device */ 38562306a36Sopenharmony_ci if (pci_uses_irq(dev, *irq)) 38662306a36Sopenharmony_ci return 0; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci /* check if the resource is already in use, skip if the 38962306a36Sopenharmony_ci * device is active because it itself may be in use */ 39062306a36Sopenharmony_ci if (!dev->active) { 39162306a36Sopenharmony_ci if (request_irq(*irq, pnp_test_handler, 39262306a36Sopenharmony_ci IRQF_PROBE_SHARED, "pnp", NULL)) 39362306a36Sopenharmony_ci return 0; 39462306a36Sopenharmony_ci free_irq(*irq, NULL); 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci /* check for conflicts with other pnp devices */ 39862306a36Sopenharmony_ci pnp_for_each_dev(tdev) { 39962306a36Sopenharmony_ci if (tdev == dev) 40062306a36Sopenharmony_ci continue; 40162306a36Sopenharmony_ci for (i = 0; 40262306a36Sopenharmony_ci (tres = pnp_get_resource(tdev, IORESOURCE_IRQ, i)); 40362306a36Sopenharmony_ci i++) { 40462306a36Sopenharmony_ci if (tres->flags & IORESOURCE_IRQ) { 40562306a36Sopenharmony_ci if (cannot_compare(tres->flags)) 40662306a36Sopenharmony_ci continue; 40762306a36Sopenharmony_ci if (tres->start == *irq) 40862306a36Sopenharmony_ci return 0; 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci return 1; 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci#ifdef CONFIG_ISA_DMA_API 41762306a36Sopenharmony_ciint pnp_check_dma(struct pnp_dev *dev, struct resource *res) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci int i; 42062306a36Sopenharmony_ci struct pnp_dev *tdev; 42162306a36Sopenharmony_ci struct resource *tres; 42262306a36Sopenharmony_ci resource_size_t *dma; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci dma = &res->start; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci /* if the resource doesn't exist, don't complain about it */ 42762306a36Sopenharmony_ci if (cannot_compare(res->flags)) 42862306a36Sopenharmony_ci return 1; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci /* check if the resource is valid */ 43162306a36Sopenharmony_ci if (*dma == 4 || *dma > 7) 43262306a36Sopenharmony_ci return 0; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci /* check if the resource is reserved */ 43562306a36Sopenharmony_ci for (i = 0; i < 8; i++) { 43662306a36Sopenharmony_ci if (pnp_reserve_dma[i] == *dma) 43762306a36Sopenharmony_ci return 0; 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci /* check for internal conflicts */ 44162306a36Sopenharmony_ci for (i = 0; (tres = pnp_get_resource(dev, IORESOURCE_DMA, i)); i++) { 44262306a36Sopenharmony_ci if (tres != res && tres->flags & IORESOURCE_DMA) { 44362306a36Sopenharmony_ci if (tres->start == *dma) 44462306a36Sopenharmony_ci return 0; 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci /* check if the resource is already in use, skip if the 44962306a36Sopenharmony_ci * device is active because it itself may be in use */ 45062306a36Sopenharmony_ci if (!dev->active) { 45162306a36Sopenharmony_ci if (request_dma(*dma, "pnp")) 45262306a36Sopenharmony_ci return 0; 45362306a36Sopenharmony_ci free_dma(*dma); 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci /* check for conflicts with other pnp devices */ 45762306a36Sopenharmony_ci pnp_for_each_dev(tdev) { 45862306a36Sopenharmony_ci if (tdev == dev) 45962306a36Sopenharmony_ci continue; 46062306a36Sopenharmony_ci for (i = 0; 46162306a36Sopenharmony_ci (tres = pnp_get_resource(tdev, IORESOURCE_DMA, i)); 46262306a36Sopenharmony_ci i++) { 46362306a36Sopenharmony_ci if (tres->flags & IORESOURCE_DMA) { 46462306a36Sopenharmony_ci if (cannot_compare(tres->flags)) 46562306a36Sopenharmony_ci continue; 46662306a36Sopenharmony_ci if (tres->start == *dma) 46762306a36Sopenharmony_ci return 0; 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci return 1; 47362306a36Sopenharmony_ci} 47462306a36Sopenharmony_ci#endif /* CONFIG_ISA_DMA_API */ 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ciunsigned long pnp_resource_type(struct resource *res) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci return res->flags & (IORESOURCE_IO | IORESOURCE_MEM | 47962306a36Sopenharmony_ci IORESOURCE_IRQ | IORESOURCE_DMA | 48062306a36Sopenharmony_ci IORESOURCE_BUS); 48162306a36Sopenharmony_ci} 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_cistruct resource *pnp_get_resource(struct pnp_dev *dev, 48462306a36Sopenharmony_ci unsigned long type, unsigned int num) 48562306a36Sopenharmony_ci{ 48662306a36Sopenharmony_ci struct pnp_resource *pnp_res; 48762306a36Sopenharmony_ci struct resource *res; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci list_for_each_entry(pnp_res, &dev->resources, list) { 49062306a36Sopenharmony_ci res = &pnp_res->res; 49162306a36Sopenharmony_ci if (pnp_resource_type(res) == type && num-- == 0) 49262306a36Sopenharmony_ci return res; 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci return NULL; 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ciEXPORT_SYMBOL(pnp_get_resource); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_cistatic struct pnp_resource *pnp_new_resource(struct pnp_dev *dev) 49962306a36Sopenharmony_ci{ 50062306a36Sopenharmony_ci struct pnp_resource *pnp_res; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci pnp_res = kzalloc(sizeof(struct pnp_resource), GFP_KERNEL); 50362306a36Sopenharmony_ci if (!pnp_res) 50462306a36Sopenharmony_ci return NULL; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci list_add_tail(&pnp_res->list, &dev->resources); 50762306a36Sopenharmony_ci return pnp_res; 50862306a36Sopenharmony_ci} 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_cistruct pnp_resource *pnp_add_resource(struct pnp_dev *dev, 51162306a36Sopenharmony_ci struct resource *res) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci struct pnp_resource *pnp_res; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci pnp_res = pnp_new_resource(dev); 51662306a36Sopenharmony_ci if (!pnp_res) { 51762306a36Sopenharmony_ci dev_err(&dev->dev, "can't add resource %pR\n", res); 51862306a36Sopenharmony_ci return NULL; 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci pnp_res->res = *res; 52262306a36Sopenharmony_ci pnp_res->res.name = dev->name; 52362306a36Sopenharmony_ci dev_dbg(&dev->dev, "%pR\n", res); 52462306a36Sopenharmony_ci return pnp_res; 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_cistruct pnp_resource *pnp_add_irq_resource(struct pnp_dev *dev, int irq, 52862306a36Sopenharmony_ci int flags) 52962306a36Sopenharmony_ci{ 53062306a36Sopenharmony_ci struct pnp_resource *pnp_res; 53162306a36Sopenharmony_ci struct resource *res; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci pnp_res = pnp_new_resource(dev); 53462306a36Sopenharmony_ci if (!pnp_res) { 53562306a36Sopenharmony_ci dev_err(&dev->dev, "can't add resource for IRQ %d\n", irq); 53662306a36Sopenharmony_ci return NULL; 53762306a36Sopenharmony_ci } 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci res = &pnp_res->res; 54062306a36Sopenharmony_ci res->flags = IORESOURCE_IRQ | flags; 54162306a36Sopenharmony_ci res->start = irq; 54262306a36Sopenharmony_ci res->end = irq; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci dev_dbg(&dev->dev, "%pR\n", res); 54562306a36Sopenharmony_ci return pnp_res; 54662306a36Sopenharmony_ci} 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_cistruct pnp_resource *pnp_add_dma_resource(struct pnp_dev *dev, int dma, 54962306a36Sopenharmony_ci int flags) 55062306a36Sopenharmony_ci{ 55162306a36Sopenharmony_ci struct pnp_resource *pnp_res; 55262306a36Sopenharmony_ci struct resource *res; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci pnp_res = pnp_new_resource(dev); 55562306a36Sopenharmony_ci if (!pnp_res) { 55662306a36Sopenharmony_ci dev_err(&dev->dev, "can't add resource for DMA %d\n", dma); 55762306a36Sopenharmony_ci return NULL; 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci res = &pnp_res->res; 56162306a36Sopenharmony_ci res->flags = IORESOURCE_DMA | flags; 56262306a36Sopenharmony_ci res->start = dma; 56362306a36Sopenharmony_ci res->end = dma; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci dev_printk(KERN_DEBUG, &dev->dev, "%pR\n", res); 56662306a36Sopenharmony_ci return pnp_res; 56762306a36Sopenharmony_ci} 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_cistruct pnp_resource *pnp_add_io_resource(struct pnp_dev *dev, 57062306a36Sopenharmony_ci resource_size_t start, 57162306a36Sopenharmony_ci resource_size_t end, int flags) 57262306a36Sopenharmony_ci{ 57362306a36Sopenharmony_ci struct pnp_resource *pnp_res; 57462306a36Sopenharmony_ci struct resource *res; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci pnp_res = pnp_new_resource(dev); 57762306a36Sopenharmony_ci if (!pnp_res) { 57862306a36Sopenharmony_ci dev_err(&dev->dev, "can't add resource for IO %#llx-%#llx\n", 57962306a36Sopenharmony_ci (unsigned long long) start, 58062306a36Sopenharmony_ci (unsigned long long) end); 58162306a36Sopenharmony_ci return NULL; 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci res = &pnp_res->res; 58562306a36Sopenharmony_ci res->flags = IORESOURCE_IO | flags; 58662306a36Sopenharmony_ci res->start = start; 58762306a36Sopenharmony_ci res->end = end; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci dev_printk(KERN_DEBUG, &dev->dev, "%pR\n", res); 59062306a36Sopenharmony_ci return pnp_res; 59162306a36Sopenharmony_ci} 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_cistruct pnp_resource *pnp_add_mem_resource(struct pnp_dev *dev, 59462306a36Sopenharmony_ci resource_size_t start, 59562306a36Sopenharmony_ci resource_size_t end, int flags) 59662306a36Sopenharmony_ci{ 59762306a36Sopenharmony_ci struct pnp_resource *pnp_res; 59862306a36Sopenharmony_ci struct resource *res; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci pnp_res = pnp_new_resource(dev); 60162306a36Sopenharmony_ci if (!pnp_res) { 60262306a36Sopenharmony_ci dev_err(&dev->dev, "can't add resource for MEM %#llx-%#llx\n", 60362306a36Sopenharmony_ci (unsigned long long) start, 60462306a36Sopenharmony_ci (unsigned long long) end); 60562306a36Sopenharmony_ci return NULL; 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci res = &pnp_res->res; 60962306a36Sopenharmony_ci res->flags = IORESOURCE_MEM | flags; 61062306a36Sopenharmony_ci res->start = start; 61162306a36Sopenharmony_ci res->end = end; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci dev_printk(KERN_DEBUG, &dev->dev, "%pR\n", res); 61462306a36Sopenharmony_ci return pnp_res; 61562306a36Sopenharmony_ci} 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_cistruct pnp_resource *pnp_add_bus_resource(struct pnp_dev *dev, 61862306a36Sopenharmony_ci resource_size_t start, 61962306a36Sopenharmony_ci resource_size_t end) 62062306a36Sopenharmony_ci{ 62162306a36Sopenharmony_ci struct pnp_resource *pnp_res; 62262306a36Sopenharmony_ci struct resource *res; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci pnp_res = pnp_new_resource(dev); 62562306a36Sopenharmony_ci if (!pnp_res) { 62662306a36Sopenharmony_ci dev_err(&dev->dev, "can't add resource for BUS %#llx-%#llx\n", 62762306a36Sopenharmony_ci (unsigned long long) start, 62862306a36Sopenharmony_ci (unsigned long long) end); 62962306a36Sopenharmony_ci return NULL; 63062306a36Sopenharmony_ci } 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci res = &pnp_res->res; 63362306a36Sopenharmony_ci res->flags = IORESOURCE_BUS; 63462306a36Sopenharmony_ci res->start = start; 63562306a36Sopenharmony_ci res->end = end; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci dev_printk(KERN_DEBUG, &dev->dev, "%pR\n", res); 63862306a36Sopenharmony_ci return pnp_res; 63962306a36Sopenharmony_ci} 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci/* 64262306a36Sopenharmony_ci * Determine whether the specified resource is a possible configuration 64362306a36Sopenharmony_ci * for this device. 64462306a36Sopenharmony_ci */ 64562306a36Sopenharmony_ciint pnp_possible_config(struct pnp_dev *dev, int type, resource_size_t start, 64662306a36Sopenharmony_ci resource_size_t size) 64762306a36Sopenharmony_ci{ 64862306a36Sopenharmony_ci struct pnp_option *option; 64962306a36Sopenharmony_ci struct pnp_port *port; 65062306a36Sopenharmony_ci struct pnp_mem *mem; 65162306a36Sopenharmony_ci struct pnp_irq *irq; 65262306a36Sopenharmony_ci struct pnp_dma *dma; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci list_for_each_entry(option, &dev->options, list) { 65562306a36Sopenharmony_ci if (option->type != type) 65662306a36Sopenharmony_ci continue; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci switch (option->type) { 65962306a36Sopenharmony_ci case IORESOURCE_IO: 66062306a36Sopenharmony_ci port = &option->u.port; 66162306a36Sopenharmony_ci if (port->min == start && port->size == size) 66262306a36Sopenharmony_ci return 1; 66362306a36Sopenharmony_ci break; 66462306a36Sopenharmony_ci case IORESOURCE_MEM: 66562306a36Sopenharmony_ci mem = &option->u.mem; 66662306a36Sopenharmony_ci if (mem->min == start && mem->size == size) 66762306a36Sopenharmony_ci return 1; 66862306a36Sopenharmony_ci break; 66962306a36Sopenharmony_ci case IORESOURCE_IRQ: 67062306a36Sopenharmony_ci irq = &option->u.irq; 67162306a36Sopenharmony_ci if (start < PNP_IRQ_NR && 67262306a36Sopenharmony_ci test_bit(start, irq->map.bits)) 67362306a36Sopenharmony_ci return 1; 67462306a36Sopenharmony_ci break; 67562306a36Sopenharmony_ci case IORESOURCE_DMA: 67662306a36Sopenharmony_ci dma = &option->u.dma; 67762306a36Sopenharmony_ci if (dma->map & (1 << start)) 67862306a36Sopenharmony_ci return 1; 67962306a36Sopenharmony_ci break; 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci } 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci return 0; 68462306a36Sopenharmony_ci} 68562306a36Sopenharmony_ciEXPORT_SYMBOL(pnp_possible_config); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ciint pnp_range_reserved(resource_size_t start, resource_size_t end) 68862306a36Sopenharmony_ci{ 68962306a36Sopenharmony_ci struct pnp_dev *dev; 69062306a36Sopenharmony_ci struct pnp_resource *pnp_res; 69162306a36Sopenharmony_ci resource_size_t *dev_start, *dev_end; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci pnp_for_each_dev(dev) { 69462306a36Sopenharmony_ci list_for_each_entry(pnp_res, &dev->resources, list) { 69562306a36Sopenharmony_ci dev_start = &pnp_res->res.start; 69662306a36Sopenharmony_ci dev_end = &pnp_res->res.end; 69762306a36Sopenharmony_ci if (ranged_conflict(&start, &end, dev_start, dev_end)) 69862306a36Sopenharmony_ci return 1; 69962306a36Sopenharmony_ci } 70062306a36Sopenharmony_ci } 70162306a36Sopenharmony_ci return 0; 70262306a36Sopenharmony_ci} 70362306a36Sopenharmony_ciEXPORT_SYMBOL(pnp_range_reserved); 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci/* format is: pnp_reserve_irq=irq1[,irq2] .... */ 70662306a36Sopenharmony_cistatic int __init pnp_setup_reserve_irq(char *str) 70762306a36Sopenharmony_ci{ 70862306a36Sopenharmony_ci int i; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci for (i = 0; i < 16; i++) 71162306a36Sopenharmony_ci if (get_option(&str, &pnp_reserve_irq[i]) != 2) 71262306a36Sopenharmony_ci break; 71362306a36Sopenharmony_ci return 1; 71462306a36Sopenharmony_ci} 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci__setup("pnp_reserve_irq=", pnp_setup_reserve_irq); 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci/* format is: pnp_reserve_dma=dma1[,dma2] .... */ 71962306a36Sopenharmony_cistatic int __init pnp_setup_reserve_dma(char *str) 72062306a36Sopenharmony_ci{ 72162306a36Sopenharmony_ci int i; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci for (i = 0; i < 8; i++) 72462306a36Sopenharmony_ci if (get_option(&str, &pnp_reserve_dma[i]) != 2) 72562306a36Sopenharmony_ci break; 72662306a36Sopenharmony_ci return 1; 72762306a36Sopenharmony_ci} 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci__setup("pnp_reserve_dma=", pnp_setup_reserve_dma); 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci/* format is: pnp_reserve_io=io1,size1[,io2,size2] .... */ 73262306a36Sopenharmony_cistatic int __init pnp_setup_reserve_io(char *str) 73362306a36Sopenharmony_ci{ 73462306a36Sopenharmony_ci int i; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci for (i = 0; i < 16; i++) 73762306a36Sopenharmony_ci if (get_option(&str, &pnp_reserve_io[i]) != 2) 73862306a36Sopenharmony_ci break; 73962306a36Sopenharmony_ci return 1; 74062306a36Sopenharmony_ci} 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci__setup("pnp_reserve_io=", pnp_setup_reserve_io); 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci/* format is: pnp_reserve_mem=mem1,size1[,mem2,size2] .... */ 74562306a36Sopenharmony_cistatic int __init pnp_setup_reserve_mem(char *str) 74662306a36Sopenharmony_ci{ 74762306a36Sopenharmony_ci int i; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci for (i = 0; i < 16; i++) 75062306a36Sopenharmony_ci if (get_option(&str, &pnp_reserve_mem[i]) != 2) 75162306a36Sopenharmony_ci break; 75262306a36Sopenharmony_ci return 1; 75362306a36Sopenharmony_ci} 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci__setup("pnp_reserve_mem=", pnp_setup_reserve_mem); 756