*
* 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/errno.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/pnp.h>
-#include <linux/slab.h>
#include <linux/bitmap.h>
#include <linux/mutex.h>
#include "base.h"
res = pnp_get_resource(dev, IORESOURCE_IO, idx);
if (res) {
- dev_dbg(&dev->dev, " io %d already set to %#llx-%#llx "
+ pnp_dbg(&dev->dev, " io %d already set to %#llx-%#llx "
"flags %#lx\n", idx, (unsigned long long) res->start,
(unsigned long long) res->end, res->flags);
- return 1;
+ return 0;
}
res = &local_res;
if (!rule->size) {
res->flags |= IORESOURCE_DISABLED;
- dev_dbg(&dev->dev, " io %d disabled\n", idx);
+ pnp_dbg(&dev->dev, " io %d disabled\n", idx);
goto __add;
}
res->start += rule->align;
res->end = res->start + rule->size - 1;
if (res->start > rule->max || !rule->align) {
- dev_dbg(&dev->dev, " couldn't assign io %d "
+ pnp_dbg(&dev->dev, " couldn't assign io %d "
"(min %#llx max %#llx)\n", idx,
(unsigned long long) rule->min,
(unsigned long long) rule->max);
- return 0;
+ return -EBUSY;
}
}
__add:
pnp_add_io_resource(dev, res->start, res->end, res->flags);
- return 1;
+ return 0;
}
static int pnp_assign_mem(struct pnp_dev *dev, struct pnp_mem *rule, int idx)
res = pnp_get_resource(dev, IORESOURCE_MEM, idx);
if (res) {
- dev_dbg(&dev->dev, " mem %d already set to %#llx-%#llx "
+ pnp_dbg(&dev->dev, " mem %d already set to %#llx-%#llx "
"flags %#lx\n", idx, (unsigned long long) res->start,
(unsigned long long) res->end, res->flags);
- return 1;
+ return 0;
}
res = &local_res;
if (!rule->size) {
res->flags |= IORESOURCE_DISABLED;
- dev_dbg(&dev->dev, " mem %d disabled\n", idx);
+ pnp_dbg(&dev->dev, " mem %d disabled\n", idx);
goto __add;
}
res->start += rule->align;
res->end = res->start + rule->size - 1;
if (res->start > rule->max || !rule->align) {
- dev_dbg(&dev->dev, " couldn't assign mem %d "
+ pnp_dbg(&dev->dev, " couldn't assign mem %d "
"(min %#llx max %#llx)\n", idx,
(unsigned long long) rule->min,
(unsigned long long) rule->max);
- return 0;
+ return -EBUSY;
}
}
__add:
pnp_add_mem_resource(dev, res->start, res->end, res->flags);
- return 1;
+ return 0;
}
static int pnp_assign_irq(struct pnp_dev *dev, struct pnp_irq *rule, int idx)
res = pnp_get_resource(dev, IORESOURCE_IRQ, idx);
if (res) {
- dev_dbg(&dev->dev, " irq %d already set to %d flags %#lx\n",
+ pnp_dbg(&dev->dev, " irq %d already set to %d flags %#lx\n",
idx, (int) res->start, res->flags);
- return 1;
+ return 0;
}
res = &local_res;
if (bitmap_empty(rule->map.bits, PNP_IRQ_NR)) {
res->flags |= IORESOURCE_DISABLED;
- dev_dbg(&dev->dev, " irq %d disabled\n", idx);
+ pnp_dbg(&dev->dev, " irq %d disabled\n", idx);
goto __add;
}
goto __add;
}
}
- dev_dbg(&dev->dev, " couldn't assign irq %d\n", idx);
- return 0;
+
+ if (rule->flags & IORESOURCE_IRQ_OPTIONAL) {
+ res->start = -1;
+ res->end = -1;
+ res->flags |= IORESOURCE_DISABLED;
+ pnp_dbg(&dev->dev, " irq %d disabled (optional)\n", idx);
+ goto __add;
+ }
+
+ pnp_dbg(&dev->dev, " couldn't assign irq %d\n", idx);
+ return -EBUSY;
__add:
pnp_add_irq_resource(dev, res->start, res->flags);
- return 1;
+ return 0;
}
-static void pnp_assign_dma(struct pnp_dev *dev, struct pnp_dma *rule, int idx)
+static int pnp_assign_dma(struct pnp_dev *dev, struct pnp_dma *rule, int idx)
{
struct resource *res, local_res;
int i;
res = pnp_get_resource(dev, IORESOURCE_DMA, idx);
if (res) {
- dev_dbg(&dev->dev, " dma %d already set to %d flags %#lx\n",
+ pnp_dbg(&dev->dev, " dma %d already set to %d flags %#lx\n",
idx, (int) res->start, res->flags);
- return;
+ return 0;
}
res = &local_res;
res->start = res->end = MAX_DMA_CHANNELS;
#endif
res->flags |= IORESOURCE_DISABLED;
- dev_dbg(&dev->dev, " disable dma %d\n", idx);
+ pnp_dbg(&dev->dev, " disable dma %d\n", idx);
__add:
pnp_add_dma_resource(dev, res->start, res->flags);
+ return 0;
}
void pnp_init_resources(struct pnp_dev *dev)
/**
* 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.
+ * @set: the dependent function number
*/
-static int pnp_assign_resources(struct pnp_dev *dev, int depnum)
+static int pnp_assign_resources(struct pnp_dev *dev, int set)
{
- struct pnp_port *port;
- struct pnp_mem *mem;
- struct pnp_irq *irq;
- struct pnp_dma *dma;
+ struct pnp_option *option;
int nport = 0, nmem = 0, nirq = 0, ndma = 0;
+ int ret = 0;
- if (!pnp_can_configure(dev))
- return -ENODEV;
-
- dbg_pnp_show_resources(dev, "before pnp_assign_resources");
+ pnp_dbg(&dev->dev, "pnp_assign_resources, try dependent set %d\n", set);
mutex_lock(&pnp_res_mutex);
pnp_clean_resource_table(dev);
- if (dev->independent) {
- dev_dbg(&dev->dev, "assigning independent options\n");
- 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) {
- pnp_assign_dma(dev, dma, ndma);
- ndma++;
- dma = dma->next;
- }
- }
- if (depnum) {
- struct pnp_option *dep;
- int i;
-
- dev_dbg(&dev->dev, "assigning dependent option %d\n", depnum);
- 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;
+ list_for_each_entry(option, &dev->options, list) {
+ if (pnp_option_is_dependent(option) &&
+ pnp_option_set(option) != set)
+ continue;
+
+ switch (option->type) {
+ case IORESOURCE_IO:
+ ret = pnp_assign_port(dev, &option->u.port, nport++);
+ break;
+ case IORESOURCE_MEM:
+ ret = pnp_assign_mem(dev, &option->u.mem, nmem++);
+ break;
+ case IORESOURCE_IRQ:
+ ret = pnp_assign_irq(dev, &option->u.irq, nirq++);
+ break;
+ case IORESOURCE_DMA:
+ ret = pnp_assign_dma(dev, &option->u.dma, ndma++);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
}
- while (dma) {
- pnp_assign_dma(dev, dma, ndma);
- ndma++;
- dma = dma->next;
- }
- } else if (dev->dependent)
- goto fail;
-
- mutex_unlock(&pnp_res_mutex);
- dbg_pnp_show_resources(dev, "after pnp_assign_resources");
- return 1;
+ if (ret < 0)
+ break;
+ }
-fail:
- pnp_clean_resource_table(dev);
mutex_unlock(&pnp_res_mutex);
- dbg_pnp_show_resources(dev, "after pnp_assign_resources (failed)");
- return 0;
+ if (ret < 0) {
+ pnp_dbg(&dev->dev, "pnp_assign_resources failed (%d)\n", ret);
+ pnp_clean_resource_table(dev);
+ } else
+ dbg_pnp_show_resources(dev, "pnp_assign_resources succeeded");
+ return ret;
}
/**
*/
int pnp_auto_config_dev(struct pnp_dev *dev)
{
- struct pnp_option *dep;
- int i = 1;
+ int i, ret;
if (!pnp_can_configure(dev)) {
- dev_dbg(&dev->dev, "configuration not supported\n");
+ pnp_dbg(&dev->dev, "configuration not supported\n");
return -ENODEV;
}
- if (!dev->dependent) {
- if (pnp_assign_resources(dev, 0))
+ ret = pnp_assign_resources(dev, 0);
+ if (ret == 0)
+ return 0;
+
+ for (i = 1; i < dev->num_dependent_sets; i++) {
+ ret = pnp_assign_resources(dev, i);
+ if (ret == 0)
return 0;
- } else {
- dep = dev->dependent;
- do {
- if (pnp_assign_resources(dev, i))
- return 0;
- dep = dep->next;
- i++;
- } while (dep);
}
dev_err(&dev->dev, "unable to assign resources\n");
- return -EBUSY;
+ return ret;
}
/**
int pnp_start_dev(struct pnp_dev *dev)
{
if (!pnp_can_write(dev)) {
- dev_dbg(&dev->dev, "activation not supported\n");
+ pnp_dbg(&dev->dev, "activation not supported\n");
return -EINVAL;
}
int pnp_stop_dev(struct pnp_dev *dev)
{
if (!pnp_can_disable(dev)) {
- dev_dbg(&dev->dev, "disabling not supported\n");
+ pnp_dbg(&dev->dev, "disabling not supported\n");
return -EINVAL;
}
if (dev->protocol->disable(dev) < 0) {