755 lines · 16.7 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * resource.c - Contains functions for registering and analyzing resource information * * based on isapnp.c resource management (c) Jaroslav Kysela <perex@perex.cz> * Copyright 2003 Adam Belay <ambx1@neo.rr.com> * Copyright (C) 2008 Hewlett-Packard Development Company, L.P. *	Bjorn Helgaas <bjorn.helgaas@hp.com> */ #include <linux/module.h>#include <linux/slab.h>#include <linux/errno.h>#include <linux/interrupt.h>#include <linux/kernel.h>#include <asm/io.h>#include <asm/dma.h>#include <asm/irq.h>#include <linux/pci.h>#include <linux/libata.h>#include <linux/ioport.h>#include <linux/init.h> #include <linux/pnp.h>#include "base.h" static int pnp_reserve_irq[16] = {[0 ... 15] = -1 };	/* reserve (don't use) some IRQ */static int pnp_reserve_dma[8] = {[0 ... 7] = -1 };	/* reserve (don't use) some DMA */static int pnp_reserve_io[16] = {[0 ... 15] = -1 };	/* reserve (don't use) some I/O region */static int pnp_reserve_mem[16] = {[0 ... 15] = -1 };	/* reserve (don't use) some memory region */ /* * option registration */ static struct pnp_option *pnp_build_option(struct pnp_dev *dev, unsigned long type,				    unsigned int option_flags){	struct pnp_option *option; 	option = kzalloc_obj(struct pnp_option);	if (!option)		return NULL; 	option->flags = option_flags;	option->type = type; 	list_add_tail(&option->list, &dev->options);	return option;} int pnp_register_irq_resource(struct pnp_dev *dev, unsigned int option_flags,			      pnp_irq_mask_t *map, unsigned char flags){	struct pnp_option *option;	struct pnp_irq *irq; 	option = pnp_build_option(dev, IORESOURCE_IRQ, option_flags);	if (!option)		return -ENOMEM; 	irq = &option->u.irq;	irq->map = *map;	irq->flags = flags; #ifdef CONFIG_PCI	{		int i; 		for (i = 0; i < 16; i++)			if (test_bit(i, irq->map.bits))				pcibios_penalize_isa_irq(i, 0);	}#endif 	dbg_pnp_show_option(dev, option);	return 0;} int pnp_register_dma_resource(struct pnp_dev *dev, unsigned int option_flags,			      unsigned char map, unsigned char flags){	struct pnp_option *option;	struct pnp_dma *dma; 	option = pnp_build_option(dev, IORESOURCE_DMA, option_flags);	if (!option)		return -ENOMEM; 	dma = &option->u.dma;	dma->map = map;	dma->flags = flags; 	dbg_pnp_show_option(dev, option);	return 0;} int pnp_register_port_resource(struct pnp_dev *dev, unsigned int option_flags,			       resource_size_t min, resource_size_t max,			       resource_size_t align, resource_size_t size,			       unsigned char flags){	struct pnp_option *option;	struct pnp_port *port; 	option = pnp_build_option(dev, IORESOURCE_IO, option_flags);	if (!option)		return -ENOMEM; 	port = &option->u.port;	port->min = min;	port->max = max;	port->align = align;	port->size = size;	port->flags = flags; 	dbg_pnp_show_option(dev, option);	return 0;} int pnp_register_mem_resource(struct pnp_dev *dev, unsigned int option_flags,			      resource_size_t min, resource_size_t max,			      resource_size_t align, resource_size_t size,			      unsigned char flags){	struct pnp_option *option;	struct pnp_mem *mem; 	option = pnp_build_option(dev, IORESOURCE_MEM, option_flags);	if (!option)		return -ENOMEM; 	mem = &option->u.mem;	mem->min = min;	mem->max = max;	mem->align = align;	mem->size = size;	mem->flags = flags; 	dbg_pnp_show_option(dev, option);	return 0;} void pnp_free_options(struct pnp_dev *dev){	struct pnp_option *option, *tmp; 	list_for_each_entry_safe(option, tmp, &dev->options, list) {		list_del(&option->list);		kfree(option);	}} /* * resource validity checking */ #define length(start, end) (*(end) - *(start) + 1) /* Two ranges conflict if one doesn't end before the other starts */#define ranged_conflict(starta, enda, startb, endb) \	!((*(enda) < *(startb)) || (*(endb) < *(starta))) #define cannot_compare(flags) \((flags) & IORESOURCE_DISABLED) int pnp_check_port(struct pnp_dev *dev, struct resource *res){	int i;	struct pnp_dev *tdev;	struct resource *tres;	resource_size_t *port, *end, *tport, *tend; 	port = &res->start;	end = &res->end; 	/* if the resource doesn't exist, don't complain about it */	if (cannot_compare(res->flags))		return 1; 	/* check if the resource is already in use, skip if the	 * device is active because it itself may be in use */	if (!dev->active) {		if (!request_region(*port, length(port, end), "pnp"))			return 0;		release_region(*port, length(port, end));	} 	/* check if the resource is reserved */	for (i = 0; i < 8; i++) {		int rport = pnp_reserve_io[i << 1];		int rend = pnp_reserve_io[(i << 1) + 1] + rport - 1;		if (ranged_conflict(port, end, &rport, &rend))			return 0;	} 	/* check for internal conflicts */	for (i = 0; (tres = pnp_get_resource(dev, IORESOURCE_IO, i)); i++) {		if (tres != res && tres->flags & IORESOURCE_IO) {			tport = &tres->start;			tend = &tres->end;			if (ranged_conflict(port, end, tport, tend))				return 0;		}	} 	/* check for conflicts with other pnp devices */	pnp_for_each_dev(tdev) {		if (tdev == dev)			continue;		for (i = 0;		     (tres = pnp_get_resource(tdev, IORESOURCE_IO, i));		     i++) {			if (tres->flags & IORESOURCE_IO) {				if (cannot_compare(tres->flags))					continue;				if (tres->flags & IORESOURCE_WINDOW)					continue;				tport = &tres->start;				tend = &tres->end;				if (ranged_conflict(port, end, tport, tend))					return 0;			}		}	} 	return 1;} int pnp_check_mem(struct pnp_dev *dev, struct resource *res){	int i;	struct pnp_dev *tdev;	struct resource *tres;	resource_size_t *addr, *end, *taddr, *tend; 	addr = &res->start;	end = &res->end; 	/* if the resource doesn't exist, don't complain about it */	if (cannot_compare(res->flags))		return 1; 	/* check if the resource is already in use, skip if the	 * device is active because it itself may be in use */	if (!dev->active) {		if (!request_mem_region(*addr, length(addr, end), "pnp"))			return 0;		release_mem_region(*addr, length(addr, end));	} 	/* check if the resource is reserved */	for (i = 0; i < 8; i++) {		int raddr = pnp_reserve_mem[i << 1];		int rend = pnp_reserve_mem[(i << 1) + 1] + raddr - 1;		if (ranged_conflict(addr, end, &raddr, &rend))			return 0;	} 	/* check for internal conflicts */	for (i = 0; (tres = pnp_get_resource(dev, IORESOURCE_MEM, i)); i++) {		if (tres != res && tres->flags & IORESOURCE_MEM) {			taddr = &tres->start;			tend = &tres->end;			if (ranged_conflict(addr, end, taddr, tend))				return 0;		}	} 	/* check for conflicts with other pnp devices */	pnp_for_each_dev(tdev) {		if (tdev == dev)			continue;		for (i = 0;		     (tres = pnp_get_resource(tdev, IORESOURCE_MEM, i));		     i++) {			if (tres->flags & IORESOURCE_MEM) {				if (cannot_compare(tres->flags))					continue;				if (tres->flags & IORESOURCE_WINDOW)					continue;				taddr = &tres->start;				tend = &tres->end;				if (ranged_conflict(addr, end, taddr, tend))					return 0;			}		}	} 	return 1;} static irqreturn_t pnp_test_handler(int irq, void *dev_id){	return IRQ_HANDLED;} #ifdef CONFIG_PCIstatic int pci_dev_uses_irq(struct pnp_dev *pnp, struct pci_dev *pci,			    unsigned int irq){	u32 class;	u8 progif; 	if (pci->irq == irq) {		pnp_dbg(&pnp->dev, "  device %s using irq %d\n",			pci_name(pci), irq);		return 1;	} 	/*	 * See pci_setup_device() and ata_pci_sff_activate_host() for	 * similar IDE legacy detection.	 */	pci_read_config_dword(pci, PCI_CLASS_REVISION, &class);	class >>= 8;		/* discard revision ID */	progif = class & 0xff;	class >>= 8; 	if (class == PCI_CLASS_STORAGE_IDE) {		/*		 * Unless both channels are native-PCI mode only,		 * treat the compatibility IRQs as busy.		 */		if ((progif & 0x5) != 0x5)			if (ATA_PRIMARY_IRQ(pci) == irq ||			    ATA_SECONDARY_IRQ(pci) == irq) {				pnp_dbg(&pnp->dev, "  legacy IDE device %s "					"using irq %d\n", pci_name(pci), irq);				return 1;			}	} 	return 0;}#endif static int pci_uses_irq(struct pnp_dev *pnp, unsigned int irq){#ifdef CONFIG_PCI	struct pci_dev *pci = NULL; 	for_each_pci_dev(pci) {		if (pci_dev_uses_irq(pnp, pci, irq)) {			pci_dev_put(pci);			return 1;		}	}#endif	return 0;} int pnp_check_irq(struct pnp_dev *dev, struct resource *res){	int i;	struct pnp_dev *tdev;	struct resource *tres;	resource_size_t *irq; 	irq = &res->start; 	/* if the resource doesn't exist, don't complain about it */	if (cannot_compare(res->flags))		return 1; 	/* check if the resource is valid */	if (*irq > 15)		return 0; 	/* check if the resource is reserved */	for (i = 0; i < 16; i++) {		if (pnp_reserve_irq[i] == *irq)			return 0;	} 	/* check for internal conflicts */	for (i = 0; (tres = pnp_get_resource(dev, IORESOURCE_IRQ, i)); i++) {		if (tres != res && tres->flags & IORESOURCE_IRQ) {			if (tres->start == *irq)				return 0;		}	} 	/* check if the resource is being used by a pci device */	if (pci_uses_irq(dev, *irq))		return 0; 	/* check if the resource is already in use, skip if the	 * device is active because it itself may be in use */	if (!dev->active) {		if (request_irq(*irq, pnp_test_handler,				IRQF_PROBE_SHARED, "pnp", NULL))			return 0;		free_irq(*irq, NULL);	} 	/* check for conflicts with other pnp devices */	pnp_for_each_dev(tdev) {		if (tdev == dev)			continue;		for (i = 0;		     (tres = pnp_get_resource(tdev, IORESOURCE_IRQ, i));		     i++) {			if (tres->flags & IORESOURCE_IRQ) {				if (cannot_compare(tres->flags))					continue;				if (tres->start == *irq)					return 0;			}		}	} 	return 1;} #ifdef CONFIG_ISA_DMA_APIint pnp_check_dma(struct pnp_dev *dev, struct resource *res){	int i;	struct pnp_dev *tdev;	struct resource *tres;	resource_size_t *dma; 	dma = &res->start; 	/* if the resource doesn't exist, don't complain about it */	if (cannot_compare(res->flags))		return 1; 	/* check if the resource is valid */	if (*dma == 4 || *dma > 7)		return 0; 	/* check if the resource is reserved */	for (i = 0; i < 8; i++) {		if (pnp_reserve_dma[i] == *dma)			return 0;	} 	/* check for internal conflicts */	for (i = 0; (tres = pnp_get_resource(dev, IORESOURCE_DMA, i)); i++) {		if (tres != res && tres->flags & IORESOURCE_DMA) {			if (tres->start == *dma)				return 0;		}	} 	/* check if the resource is already in use, skip if the	 * device is active because it itself may be in use */	if (!dev->active) {		if (request_dma(*dma, "pnp"))			return 0;		free_dma(*dma);	} 	/* check for conflicts with other pnp devices */	pnp_for_each_dev(tdev) {		if (tdev == dev)			continue;		for (i = 0;		     (tres = pnp_get_resource(tdev, IORESOURCE_DMA, i));		     i++) {			if (tres->flags & IORESOURCE_DMA) {				if (cannot_compare(tres->flags))					continue;				if (tres->start == *dma)					return 0;			}		}	} 	return 1;}#endif /* CONFIG_ISA_DMA_API */ unsigned long pnp_resource_type(struct resource *res){	return res->flags & (IORESOURCE_IO  | IORESOURCE_MEM |			     IORESOURCE_IRQ | IORESOURCE_DMA |			     IORESOURCE_BUS);} struct resource *pnp_get_resource(struct pnp_dev *dev,				  unsigned long type, unsigned int num){	struct pnp_resource *pnp_res;	struct resource *res; 	list_for_each_entry(pnp_res, &dev->resources, list) {		res = &pnp_res->res;		if (pnp_resource_type(res) == type && num-- == 0)			return res;	}	return NULL;}EXPORT_SYMBOL(pnp_get_resource); static struct pnp_resource *pnp_new_resource(struct pnp_dev *dev){	struct pnp_resource *pnp_res;

Lines 1–500 of 755.