android_kernel_xiaomi_sm8350/drivers/pnp/manager.c
Tim Schmielau 4e57b68178 [PATCH] fix missing includes
I recently picked up my older work to remove unnecessary #includes of
sched.h, starting from a patch by Dave Jones to not include sched.h
from module.h. This reduces the number of indirect includes of sched.h
by ~300. Another ~400 pointless direct includes can be removed after
this disentangling (patch to follow later).
However, quite a few indirect includes need to be fixed up for this.

In order to feed the patches through -mm with as little disturbance as
possible, I've split out the fixes I accumulated up to now (complete for
i386 and x86_64, more archs to follow later) and post them before the real
patch.  This way this large part of the patch is kept simple with only
adding #includes, and all hunks are independent of each other.  So if any
hunk rejects or gets in the way of other patches, just drop it.  My scripts
will pick it up again in the next round.

Signed-off-by: Tim Schmielau <tim@physik3.uni-rostock.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-10-30 17:37:32 -08:00

563 lines
14 KiB
C

/*
* manager.c - Resource Management, Conflict Resolution, Activation and Disabling of Devices
*
* based on isapnp.c resource management (c) Jaroslav Kysela <perex@suse.cz>
* Copyright 2003 Adam Belay <ambx1@neo.rr.com>
*
*/
#include <linux/config.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/pnp.h>
#include <linux/slab.h>
#include <linux/bitmap.h>
#include "base.h"
DECLARE_MUTEX(pnp_res_mutex);
static int pnp_assign_port(struct pnp_dev *dev, struct pnp_port *rule, int idx)
{
unsigned long *start, *end, *flags;
if (!dev || !rule)
return -EINVAL;
if (idx >= PNP_MAX_PORT) {
pnp_err("More than 4 ports is incompatible with pnp specifications.");
/* pretend we were successful so at least the manager won't try again */
return 1;
}
/* check if this resource has been manually set, if so skip */
if (!(dev->res.port_resource[idx].flags & IORESOURCE_AUTO))
return 1;
start = &dev->res.port_resource[idx].start;
end = &dev->res.port_resource[idx].end;
flags = &dev->res.port_resource[idx].flags;
/* set the initial values */
*flags |= rule->flags | IORESOURCE_IO;
*flags &= ~IORESOURCE_UNSET;
if (!rule->size) {
*flags |= IORESOURCE_DISABLED;
return 1; /* skip disabled resource requests */
}
*start = rule->min;
*end = *start + rule->size - 1;
/* run through until pnp_check_port is happy */
while (!pnp_check_port(dev, idx)) {
*start += rule->align;
*end = *start + rule->size - 1;
if (*start > rule->max || !rule->align)
return 0;
}
return 1;
}
static int pnp_assign_mem(struct pnp_dev *dev, struct pnp_mem *rule, int idx)
{
unsigned long *start, *end, *flags;
if (!dev || !rule)
return -EINVAL;
if (idx >= PNP_MAX_MEM) {
pnp_err("More than 8 mems is incompatible with pnp specifications.");
/* pretend we were successful so at least the manager won't try again */
return 1;
}
/* check if this resource has been manually set, if so skip */
if (!(dev->res.mem_resource[idx].flags & IORESOURCE_AUTO))
return 1;
start = &dev->res.mem_resource[idx].start;
end = &dev->res.mem_resource[idx].end;
flags = &dev->res.mem_resource[idx].flags;
/* set the initial values */
*flags |= rule->flags | IORESOURCE_MEM;
*flags &= ~IORESOURCE_UNSET;
/* convert pnp flags to standard Linux flags */
if (!(rule->flags & IORESOURCE_MEM_WRITEABLE))
*flags |= IORESOURCE_READONLY;
if (rule->flags & IORESOURCE_MEM_CACHEABLE)
*flags |= IORESOURCE_CACHEABLE;
if (rule->flags & IORESOURCE_MEM_RANGELENGTH)
*flags |= IORESOURCE_RANGELENGTH;
if (rule->flags & IORESOURCE_MEM_SHADOWABLE)
*flags |= IORESOURCE_SHADOWABLE;
if (!rule->size) {
*flags |= IORESOURCE_DISABLED;
return 1; /* skip disabled resource requests */
}
*start = rule->min;
*end = *start + rule->size -1;
/* run through until pnp_check_mem is happy */
while (!pnp_check_mem(dev, idx)) {
*start += rule->align;
*end = *start + rule->size - 1;
if (*start > rule->max || !rule->align)
return 0;
}
return 1;
}
static int pnp_assign_irq(struct pnp_dev * dev, struct pnp_irq *rule, int idx)
{
unsigned long *start, *end, *flags;
int i;
/* IRQ priority: this table is good for i386 */
static unsigned short xtab[16] = {
5, 10, 11, 12, 9, 14, 15, 7, 3, 4, 13, 0, 1, 6, 8, 2
};
if (!dev || !rule)
return -EINVAL;
if (idx >= PNP_MAX_IRQ) {
pnp_err("More than 2 irqs is incompatible with pnp specifications.");
/* pretend we were successful so at least the manager won't try again */
return 1;
}
/* check if this resource has been manually set, if so skip */
if (!(dev->res.irq_resource[idx].flags & IORESOURCE_AUTO))
return 1;
start = &dev->res.irq_resource[idx].start;
end = &dev->res.irq_resource[idx].end;
flags = &dev->res.irq_resource[idx].flags;
/* set the initial values */
*flags |= rule->flags | IORESOURCE_IRQ;
*flags &= ~IORESOURCE_UNSET;
if (bitmap_empty(rule->map, PNP_IRQ_NR)) {
*flags |= IORESOURCE_DISABLED;
return 1; /* skip disabled resource requests */
}
/* TBD: need check for >16 IRQ */
*start = find_next_bit(rule->map, PNP_IRQ_NR, 16);
if (*start < PNP_IRQ_NR) {
*end = *start;
return 1;
}
for (i = 0; i < 16; i++) {
if(test_bit(xtab[i], rule->map)) {
*start = *end = xtab[i];
if(pnp_check_irq(dev, idx))
return 1;
}
}
return 0;
}
static int pnp_assign_dma(struct pnp_dev *dev, struct pnp_dma *rule, int idx)
{
unsigned long *start, *end, *flags;
int i;
/* DMA priority: this table is good for i386 */
static unsigned short xtab[8] = {
1, 3, 5, 6, 7, 0, 2, 4
};
if (!dev || !rule)
return -EINVAL;
if (idx >= PNP_MAX_DMA) {
pnp_err("More than 2 dmas is incompatible with pnp specifications.");
/* pretend we were successful so at least the manager won't try again */
return 1;
}
/* check if this resource has been manually set, if so skip */
if (!(dev->res.dma_resource[idx].flags & IORESOURCE_AUTO))
return 1;
start = &dev->res.dma_resource[idx].start;
end = &dev->res.dma_resource[idx].end;
flags = &dev->res.dma_resource[idx].flags;
/* set the initial values */
*flags |= rule->flags | IORESOURCE_DMA;
*flags &= ~IORESOURCE_UNSET;
if (!rule->map) {
*flags |= IORESOURCE_DISABLED;
return 1; /* skip disabled resource requests */
}
for (i = 0; i < 8; i++) {
if(rule->map & (1<<xtab[i])) {
*start = *end = xtab[i];
if(pnp_check_dma(dev, idx))
return 1;
}
}
return 0;
}
/**
* pnp_init_resources - Resets a resource table to default values.
* @table: pointer to the desired resource table
*
*/
void pnp_init_resource_table(struct pnp_resource_table *table)
{
int idx;
for (idx = 0; idx < PNP_MAX_IRQ; idx++) {
table->irq_resource[idx].name = NULL;
table->irq_resource[idx].start = -1;
table->irq_resource[idx].end = -1;
table->irq_resource[idx].flags = IORESOURCE_IRQ | IORESOURCE_AUTO | IORESOURCE_UNSET;
}
for (idx = 0; idx < PNP_MAX_DMA; idx++) {
table->dma_resource[idx].name = NULL;
table->dma_resource[idx].start = -1;
table->dma_resource[idx].end = -1;
table->dma_resource[idx].flags = IORESOURCE_DMA | IORESOURCE_AUTO | IORESOURCE_UNSET;
}
for (idx = 0; idx < PNP_MAX_PORT; idx++) {
table->port_resource[idx].name = NULL;
table->port_resource[idx].start = 0;
table->port_resource[idx].end = 0;
table->port_resource[idx].flags = IORESOURCE_IO | IORESOURCE_AUTO | IORESOURCE_UNSET;
}
for (idx = 0; idx < PNP_MAX_MEM; idx++) {
table->mem_resource[idx].name = NULL;
table->mem_resource[idx].start = 0;
table->mem_resource[idx].end = 0;
table->mem_resource[idx].flags = IORESOURCE_MEM | IORESOURCE_AUTO | IORESOURCE_UNSET;
}
}
/**
* pnp_clean_resources - clears resources that were not manually set
* @res: the resources to clean
*
*/
static void pnp_clean_resource_table(struct pnp_resource_table * res)
{
int idx;
for (idx = 0; idx < PNP_MAX_IRQ; idx++) {
if (!(res->irq_resource[idx].flags & IORESOURCE_AUTO))
continue;
res->irq_resource[idx].start = -1;
res->irq_resource[idx].end = -1;
res->irq_resource[idx].flags = IORESOURCE_IRQ | IORESOURCE_AUTO | IORESOURCE_UNSET;
}
for (idx = 0; idx < PNP_MAX_DMA; idx++) {
if (!(res->dma_resource[idx].flags & IORESOURCE_AUTO))
continue;
res->dma_resource[idx].start = -1;
res->dma_resource[idx].end = -1;
res->dma_resource[idx].flags = IORESOURCE_DMA | IORESOURCE_AUTO | IORESOURCE_UNSET;
}
for (idx = 0; idx < PNP_MAX_PORT; idx++) {
if (!(res->port_resource[idx].flags & IORESOURCE_AUTO))
continue;
res->port_resource[idx].start = 0;
res->port_resource[idx].end = 0;
res->port_resource[idx].flags = IORESOURCE_IO | IORESOURCE_AUTO | IORESOURCE_UNSET;
}
for (idx = 0; idx < PNP_MAX_MEM; idx++) {
if (!(res->mem_resource[idx].flags & IORESOURCE_AUTO))
continue;
res->mem_resource[idx].start = 0;
res->mem_resource[idx].end = 0;
res->mem_resource[idx].flags = IORESOURCE_MEM | IORESOURCE_AUTO | IORESOURCE_UNSET;
}
}
/**
* pnp_assign_resources - assigns resources to the device based on the specified dependent number
* @dev: pointer to the desired device
* @depnum: the dependent function number
*
* Only set depnum to 0 if the device does not have dependent options.
*/
static int pnp_assign_resources(struct pnp_dev *dev, int depnum)
{
struct pnp_port *port;
struct pnp_mem *mem;
struct pnp_irq *irq;
struct pnp_dma *dma;
int nport = 0, nmem = 0, nirq = 0, ndma = 0;
if (!pnp_can_configure(dev))
return -ENODEV;
down(&pnp_res_mutex);
pnp_clean_resource_table(&dev->res); /* start with a fresh slate */
if (dev->independent) {
port = dev->independent->port;
mem = dev->independent->mem;
irq = dev->independent->irq;
dma = dev->independent->dma;
while (port) {
if (!pnp_assign_port(dev, port, nport))
goto fail;
nport++;
port = port->next;
}
while (mem) {
if (!pnp_assign_mem(dev, mem, nmem))
goto fail;
nmem++;
mem = mem->next;
}
while (irq) {
if (!pnp_assign_irq(dev, irq, nirq))
goto fail;
nirq++;
irq = irq->next;
}
while (dma) {
if (!pnp_assign_dma(dev, dma, ndma))
goto fail;
ndma++;
dma = dma->next;
}
}
if (depnum) {
struct pnp_option *dep;
int i;
for (i=1,dep=dev->dependent; i<depnum; i++, dep=dep->next)
if(!dep)
goto fail;
port =dep->port;
mem = dep->mem;
irq = dep->irq;
dma = dep->dma;
while (port) {
if (!pnp_assign_port(dev, port, nport))
goto fail;
nport++;
port = port->next;
}
while (mem) {
if (!pnp_assign_mem(dev, mem, nmem))
goto fail;
nmem++;
mem = mem->next;
}
while (irq) {
if (!pnp_assign_irq(dev, irq, nirq))
goto fail;
nirq++;
irq = irq->next;
}
while (dma) {
if (!pnp_assign_dma(dev, dma, ndma))
goto fail;
ndma++;
dma = dma->next;
}
} else if (dev->dependent)
goto fail;
up(&pnp_res_mutex);
return 1;
fail:
pnp_clean_resource_table(&dev->res);
up(&pnp_res_mutex);
return 0;
}
/**
* pnp_manual_config_dev - Disables Auto Config and Manually sets the resource table
* @dev: pointer to the desired device
* @res: pointer to the new resource config
* @mode: 0 or PNP_CONFIG_FORCE
*
* This function can be used by drivers that want to manually set thier resources.
*/
int pnp_manual_config_dev(struct pnp_dev *dev, struct pnp_resource_table * res, int mode)
{
int i;
struct pnp_resource_table * bak;
if (!dev || !res)
return -EINVAL;
if (!pnp_can_configure(dev))
return -ENODEV;
bak = pnp_alloc(sizeof(struct pnp_resource_table));
if (!bak)
return -ENOMEM;
*bak = dev->res;
down(&pnp_res_mutex);
dev->res = *res;
if (!(mode & PNP_CONFIG_FORCE)) {
for (i = 0; i < PNP_MAX_PORT; i++) {
if(!pnp_check_port(dev,i))
goto fail;
}
for (i = 0; i < PNP_MAX_MEM; i++) {
if(!pnp_check_mem(dev,i))
goto fail;
}
for (i = 0; i < PNP_MAX_IRQ; i++) {
if(!pnp_check_irq(dev,i))
goto fail;
}
for (i = 0; i < PNP_MAX_DMA; i++) {
if(!pnp_check_dma(dev,i))
goto fail;
}
}
up(&pnp_res_mutex);
kfree(bak);
return 0;
fail:
dev->res = *bak;
up(&pnp_res_mutex);
kfree(bak);
return -EINVAL;
}
/**
* pnp_auto_config_dev - automatically assigns resources to a device
* @dev: pointer to the desired device
*
*/
int pnp_auto_config_dev(struct pnp_dev *dev)
{
struct pnp_option *dep;
int i = 1;
if(!dev)
return -EINVAL;
if(!pnp_can_configure(dev)) {
pnp_info("Device %s does not support resource configuration.", dev->dev.bus_id);
return -ENODEV;
}
if (!dev->dependent) {
if (pnp_assign_resources(dev, 0))
return 0;
} else {
dep = dev->dependent;
do {
if (pnp_assign_resources(dev, i))
return 0;
dep = dep->next;
i++;
} while (dep);
}
pnp_err("Unable to assign resources to device %s.", dev->dev.bus_id);
return -EBUSY;
}
/**
* pnp_activate_dev - activates a PnP device for use
* @dev: pointer to the desired device
*
* does not validate or set resources so be careful.
*/
int pnp_activate_dev(struct pnp_dev *dev)
{
if (!dev)
return -EINVAL;
if (dev->active) {
return 0; /* the device is already active */
}
/* ensure resources are allocated */
if (pnp_auto_config_dev(dev))
return -EBUSY;
if (!pnp_can_write(dev)) {
pnp_info("Device %s does not supported activation.", dev->dev.bus_id);
return -EINVAL;
}
if (dev->protocol->set(dev, &dev->res)<0) {
pnp_err("Failed to activate device %s.", dev->dev.bus_id);
return -EIO;
}
dev->active = 1;
pnp_info("Device %s activated.", dev->dev.bus_id);
return 1;
}
/**
* pnp_disable_dev - disables device
* @dev: pointer to the desired device
*
* inform the correct pnp protocol so that resources can be used by other devices
*/
int pnp_disable_dev(struct pnp_dev *dev)
{
if (!dev)
return -EINVAL;
if (!dev->active) {
return 0; /* the device is already disabled */
}
if (!pnp_can_disable(dev)) {
pnp_info("Device %s does not supported disabling.", dev->dev.bus_id);
return -EINVAL;
}
if (dev->protocol->disable(dev)<0) {
pnp_err("Failed to disable device %s.", dev->dev.bus_id);
return -EIO;
}
dev->active = 0;
pnp_info("Device %s disabled.", dev->dev.bus_id);
/* release the resources so that other devices can use them */
down(&pnp_res_mutex);
pnp_clean_resource_table(&dev->res);
up(&pnp_res_mutex);
return 1;
}
/**
* pnp_resource_change - change one resource
* @resource: pointer to resource to be changed
* @start: start of region
* @size: size of region
*
*/
void pnp_resource_change(struct resource *resource, unsigned long start, unsigned long size)
{
if (resource == NULL)
return;
resource->flags &= ~(IORESOURCE_AUTO | IORESOURCE_UNSET);
resource->start = start;
resource->end = start + size - 1;
}
EXPORT_SYMBOL(pnp_manual_config_dev);
EXPORT_SYMBOL(pnp_auto_config_dev);
EXPORT_SYMBOL(pnp_activate_dev);
EXPORT_SYMBOL(pnp_disable_dev);
EXPORT_SYMBOL(pnp_resource_change);
EXPORT_SYMBOL(pnp_init_resource_table);