diff options
Diffstat (limited to 'drivers/pnp')
| -rw-r--r-- | drivers/pnp/Kconfig | 20 | ||||
| -rw-r--r-- | drivers/pnp/Makefile | 9 | ||||
| -rw-r--r-- | drivers/pnp/base.h | 186 | ||||
| -rw-r--r-- | drivers/pnp/card.c | 67 | ||||
| -rw-r--r-- | drivers/pnp/core.c | 96 | ||||
| -rw-r--r-- | drivers/pnp/driver.c | 96 | ||||
| -rw-r--r-- | drivers/pnp/interface.c | 411 | ||||
| -rw-r--r-- | drivers/pnp/isapnp/Makefile | 6 | ||||
| -rw-r--r-- | drivers/pnp/isapnp/core.c | 451 | ||||
| -rw-r--r-- | drivers/pnp/isapnp/proc.c | 37 | ||||
| -rw-r--r-- | drivers/pnp/manager.c | 567 | ||||
| -rw-r--r-- | drivers/pnp/pnpacpi/Makefile | 3 | ||||
| -rw-r--r-- | drivers/pnp/pnpacpi/core.c | 366 | ||||
| -rw-r--r-- | drivers/pnp/pnpacpi/pnpacpi.h | 9 | ||||
| -rw-r--r-- | drivers/pnp/pnpacpi/rsparser.c | 1005 | ||||
| -rw-r--r-- | drivers/pnp/pnpbios/Kconfig | 4 | ||||
| -rw-r--r-- | drivers/pnp/pnpbios/Makefile | 5 | ||||
| -rw-r--r-- | drivers/pnp/pnpbios/bioscalls.c | 43 | ||||
| -rw-r--r-- | drivers/pnp/pnpbios/core.c | 69 | ||||
| -rw-r--r-- | drivers/pnp/pnpbios/pnpbios.h | 140 | ||||
| -rw-r--r-- | drivers/pnp/pnpbios/proc.c | 214 | ||||
| -rw-r--r-- | drivers/pnp/pnpbios/rsparser.c | 545 | ||||
| -rw-r--r-- | drivers/pnp/quirks.c | 413 | ||||
| -rw-r--r-- | drivers/pnp/resource.c | 647 | ||||
| -rw-r--r-- | drivers/pnp/support.c | 151 | ||||
| -rw-r--r-- | drivers/pnp/system.c | 33 |
26 files changed, 3193 insertions, 2400 deletions
diff --git a/drivers/pnp/Kconfig b/drivers/pnp/Kconfig index 821933f9aa5..2a37b3fedb8 100644 --- a/drivers/pnp/Kconfig +++ b/drivers/pnp/Kconfig @@ -20,13 +20,21 @@ menuconfig PNP If unsure, say Y. -if PNP - -config PNP_DEBUG - bool "PnP Debug Messages" +config PNP_DEBUG_MESSAGES + default y + bool "PNP debugging messages" + depends on PNP help - Say Y if you want the Plug and Play Layer to print debug messages. - This is useful if you are developing a PnP driver or troubleshooting. + Say Y here if you want the PNP layer to be able to produce debugging + messages if needed. The messages can be enabled at boot-time with + the pnp.debug kernel parameter. + + This option allows you to save a bit of space if you do not want + the messages to even be built into the kernel. + + If you have any doubts about this, say Y here. + +if PNP comment "Protocols" diff --git a/drivers/pnp/Makefile b/drivers/pnp/Makefile index 26f5abc9c3f..bfba893cb32 100644 --- a/drivers/pnp/Makefile +++ b/drivers/pnp/Makefile @@ -2,12 +2,13 @@ # Makefile for the Linux Plug-and-Play Support. # -obj-y := core.o card.o driver.o resource.o manager.o support.o interface.o quirks.o system.o +obj-y := pnp.o + +pnp-y := core.o card.o driver.o resource.o manager.o support.o interface.o quirks.o obj-$(CONFIG_PNPACPI) += pnpacpi/ obj-$(CONFIG_PNPBIOS) += pnpbios/ obj-$(CONFIG_ISAPNP) += isapnp/ -ifeq ($(CONFIG_PNP_DEBUG),y) -EXTRA_CFLAGS += -DDEBUG -endif +# pnp_system_init goes after pnpacpi/pnpbios init +pnp-y += system.o diff --git a/drivers/pnp/base.h b/drivers/pnp/base.h index 31a633f6554..c8873b0ca55 100644 --- a/drivers/pnp/base.h +++ b/drivers/pnp/base.h @@ -1,12 +1,186 @@ +/* + * Copyright (C) 2008 Hewlett-Packard Development Company, L.P. + * Bjorn Helgaas <bjorn.helgaas@hp.com> + */ + extern spinlock_t pnp_lock; +extern const struct attribute_group *pnp_dev_groups[]; void *pnp_alloc(long size); -int pnp_interface_attach_device(struct pnp_dev *dev); + +int pnp_register_protocol(struct pnp_protocol *protocol); +void pnp_unregister_protocol(struct pnp_protocol *protocol); + +#define PNP_EISA_ID_MASK 0x7fffffff +void pnp_eisa_id_to_string(u32 id, char *str); +struct pnp_dev *pnp_alloc_dev(struct pnp_protocol *, int id, + const char *pnpid); +struct pnp_card *pnp_alloc_card(struct pnp_protocol *, int id, char *pnpid); + +int pnp_add_device(struct pnp_dev *dev); +struct pnp_id *pnp_add_id(struct pnp_dev *dev, const char *id); + +int pnp_add_card(struct pnp_card *card); +void pnp_remove_card(struct pnp_card *card); +int pnp_add_card_device(struct pnp_card *card, struct pnp_dev *dev); +void pnp_remove_card_device(struct pnp_dev *dev); + +struct pnp_port { + resource_size_t min; /* min base number */ + resource_size_t max; /* max base number */ + resource_size_t align; /* align boundary */ + resource_size_t size; /* size of range */ + unsigned char flags; /* port flags */ +}; + +#define PNP_IRQ_NR 256 +typedef struct { DECLARE_BITMAP(bits, PNP_IRQ_NR); } pnp_irq_mask_t; + +struct pnp_irq { + pnp_irq_mask_t map; /* bitmap for IRQ lines */ + unsigned char flags; /* IRQ flags */ +}; + +struct pnp_dma { + unsigned char map; /* bitmask for DMA channels */ + unsigned char flags; /* DMA flags */ +}; + +struct pnp_mem { + resource_size_t min; /* min base number */ + resource_size_t max; /* max base number */ + resource_size_t align; /* align boundary */ + resource_size_t size; /* size of range */ + unsigned char flags; /* memory flags */ +}; + +#define PNP_OPTION_DEPENDENT 0x80000000 +#define PNP_OPTION_SET_MASK 0xffff +#define PNP_OPTION_SET_SHIFT 12 +#define PNP_OPTION_PRIORITY_MASK 0xfff +#define PNP_OPTION_PRIORITY_SHIFT 0 + +#define PNP_RES_PRIORITY_PREFERRED 0 +#define PNP_RES_PRIORITY_ACCEPTABLE 1 +#define PNP_RES_PRIORITY_FUNCTIONAL 2 +#define PNP_RES_PRIORITY_INVALID PNP_OPTION_PRIORITY_MASK + +struct pnp_option { + struct list_head list; + unsigned int flags; /* independent/dependent, set, priority */ + + unsigned long type; /* IORESOURCE_{IO,MEM,IRQ,DMA} */ + union { + struct pnp_port port; + struct pnp_irq irq; + struct pnp_dma dma; + struct pnp_mem mem; + } u; +}; + +int pnp_register_irq_resource(struct pnp_dev *dev, unsigned int option_flags, + pnp_irq_mask_t *map, unsigned char flags); +int pnp_register_dma_resource(struct pnp_dev *dev, unsigned int option_flags, + unsigned char map, unsigned char flags); +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); +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); + +static inline int pnp_option_is_dependent(struct pnp_option *option) +{ + return option->flags & PNP_OPTION_DEPENDENT ? 1 : 0; +} + +static inline unsigned int pnp_option_set(struct pnp_option *option) +{ + return (option->flags >> PNP_OPTION_SET_SHIFT) & PNP_OPTION_SET_MASK; +} + +static inline unsigned int pnp_option_priority(struct pnp_option *option) +{ + return (option->flags >> PNP_OPTION_PRIORITY_SHIFT) & + PNP_OPTION_PRIORITY_MASK; +} + +static inline unsigned int pnp_new_dependent_set(struct pnp_dev *dev, + int priority) +{ + unsigned int flags; + + if (priority > PNP_RES_PRIORITY_FUNCTIONAL) { + dev_warn(&dev->dev, "invalid dependent option priority %d " + "clipped to %d", priority, + PNP_RES_PRIORITY_INVALID); + priority = PNP_RES_PRIORITY_INVALID; + } + + flags = PNP_OPTION_DEPENDENT | + ((dev->num_dependent_sets & PNP_OPTION_SET_MASK) << + PNP_OPTION_SET_SHIFT) | + ((priority & PNP_OPTION_PRIORITY_MASK) << + PNP_OPTION_PRIORITY_SHIFT); + + dev->num_dependent_sets++; + + return flags; +} + +char *pnp_option_priority_name(struct pnp_option *option); +void dbg_pnp_show_option(struct pnp_dev *dev, struct pnp_option *option); + +void pnp_init_resources(struct pnp_dev *dev); + void pnp_fixup_device(struct pnp_dev *dev); -void pnp_free_option(struct pnp_option *option); +void pnp_free_options(struct pnp_dev *dev); int __pnp_add_device(struct pnp_dev *dev); void __pnp_remove_device(struct pnp_dev *dev); -int pnp_check_port(struct pnp_dev * dev, int idx); -int pnp_check_mem(struct pnp_dev * dev, int idx); -int pnp_check_irq(struct pnp_dev * dev, int idx); -int pnp_check_dma(struct pnp_dev * dev, int idx); +int pnp_check_port(struct pnp_dev *dev, struct resource *res); +int pnp_check_mem(struct pnp_dev *dev, struct resource *res); +int pnp_check_irq(struct pnp_dev *dev, struct resource *res); +#ifdef CONFIG_ISA_DMA_API +int pnp_check_dma(struct pnp_dev *dev, struct resource *res); +#endif + +char *pnp_resource_type_name(struct resource *res); +void dbg_pnp_show_resources(struct pnp_dev *dev, char *desc); + +void pnp_free_resources(struct pnp_dev *dev); +unsigned long pnp_resource_type(struct resource *res); + +struct pnp_resource { + struct list_head list; + struct resource res; +}; + +void pnp_free_resource(struct pnp_resource *pnp_res); + +struct pnp_resource *pnp_add_resource(struct pnp_dev *dev, + struct resource *res); +struct pnp_resource *pnp_add_irq_resource(struct pnp_dev *dev, int irq, + int flags); +struct pnp_resource *pnp_add_dma_resource(struct pnp_dev *dev, int dma, + int flags); +struct pnp_resource *pnp_add_io_resource(struct pnp_dev *dev, + resource_size_t start, + resource_size_t end, int flags); +struct pnp_resource *pnp_add_mem_resource(struct pnp_dev *dev, + resource_size_t start, + resource_size_t end, int flags); +struct pnp_resource *pnp_add_bus_resource(struct pnp_dev *dev, + resource_size_t start, + resource_size_t end); + +extern int pnp_debug; + +#if defined(CONFIG_PNP_DEBUG_MESSAGES) +#define pnp_dbg(dev, format, arg...) \ + ({ if (pnp_debug) dev_printk(KERN_DEBUG, dev, format, ## arg); 0; }) +#else +#define pnp_dbg(dev, format, arg...) \ + ({ if (0) dev_printk(KERN_DEBUG, dev, format, ## arg); 0; }) +#endif diff --git a/drivers/pnp/card.c b/drivers/pnp/card.c index da1c9909eb4..874c236ac1a 100644 --- a/drivers/pnp/card.c +++ b/drivers/pnp/card.c @@ -5,8 +5,10 @@ */ #include <linux/module.h> +#include <linux/ctype.h> #include <linux/slab.h> #include <linux/pnp.h> +#include <linux/dma-mapping.h> #include "base.h" LIST_HEAD(pnp_cards); @@ -100,19 +102,33 @@ static int card_probe(struct pnp_card *card, struct pnp_card_driver *drv) * @id: pointer to a pnp_id structure * @card: pointer to the desired card */ -int pnp_add_card_id(struct pnp_id *id, struct pnp_card *card) +static struct pnp_id *pnp_add_card_id(struct pnp_card *card, char *id) { - struct pnp_id *ptr; + struct pnp_id *dev_id, *ptr; - id->next = NULL; + dev_id = kzalloc(sizeof(struct pnp_id), GFP_KERNEL); + if (!dev_id) + return NULL; + + dev_id->id[0] = id[0]; + dev_id->id[1] = id[1]; + dev_id->id[2] = id[2]; + dev_id->id[3] = tolower(id[3]); + dev_id->id[4] = tolower(id[4]); + dev_id->id[5] = tolower(id[5]); + dev_id->id[6] = tolower(id[6]); + dev_id->id[7] = '\0'; + + dev_id->next = NULL; ptr = card->id; while (ptr && ptr->next) ptr = ptr->next; if (ptr) - ptr->next = id; + ptr->next = dev_id; else - card->id = id; - return 0; + card->id = dev_id; + + return dev_id; } static void pnp_free_card_ids(struct pnp_card *card) @@ -136,6 +152,33 @@ static void pnp_release_card(struct device *dmdev) kfree(card); } +struct pnp_card *pnp_alloc_card(struct pnp_protocol *protocol, int id, char *pnpid) +{ + struct pnp_card *card; + struct pnp_id *dev_id; + + card = kzalloc(sizeof(struct pnp_card), GFP_KERNEL); + if (!card) + return NULL; + + card->protocol = protocol; + card->number = id; + + card->dev.parent = &card->protocol->dev; + dev_set_name(&card->dev, "%02x:%02x", card->protocol->number, card->number); + + card->dev.coherent_dma_mask = DMA_BIT_MASK(24); + card->dev.dma_mask = &card->dev.coherent_dma_mask; + + dev_id = pnp_add_card_id(card, pnpid); + if (!dev_id) { + kfree(card); + return NULL; + } + + return card; +} + static ssize_t pnp_show_card_name(struct device *dmdev, struct device_attribute *attr, char *buf) { @@ -191,14 +234,12 @@ int pnp_add_card(struct pnp_card *card) int error; struct list_head *pos, *temp; - sprintf(card->dev.bus_id, "%02x:%02x", card->protocol->number, - card->number); - card->dev.parent = &card->protocol->dev; card->dev.bus = NULL; card->dev.release = &pnp_release_card; error = device_register(&card->dev); if (error) { dev_err(&card->dev, "could not register (err=%d)\n", error); + put_device(&card->dev); return error; } @@ -254,8 +295,8 @@ int pnp_add_card_device(struct pnp_card *card, struct pnp_dev *dev) { dev->dev.parent = &card->dev; dev->card_link = NULL; - snprintf(dev->dev.bus_id, BUS_ID_SIZE, "%02x:%02x.%02x", - dev->protocol->number, card->number, dev->number); + dev_set_name(&dev->dev, "%02x:%02x.%02x", + dev->protocol->number, card->number, dev->number); spin_lock(&pnp_lock); dev->card = card; list_add_tail(&dev->card_list, &card->devices); @@ -280,7 +321,7 @@ void pnp_remove_card_device(struct pnp_dev *dev) * pnp_request_card_device - Searches for a PnP device under the specified card * @clink: pointer to the card link, cannot be NULL * @id: pointer to a PnP ID structure that explains the rules for finding the device - * @from: Starting place to search from. If NULL it will start from the begining. + * @from: Starting place to search from. If NULL it will start from the beginning. */ struct pnp_dev *pnp_request_card_device(struct pnp_card_link *clink, const char *id, struct pnp_dev *from) @@ -329,7 +370,7 @@ err_out: /** * pnp_release_card_device - call this when the driver no longer needs the device - * @dev: pointer to the PnP device stucture + * @dev: pointer to the PnP device structure */ void pnp_release_card_device(struct pnp_dev *dev) { diff --git a/drivers/pnp/core.c b/drivers/pnp/core.c index 7d366ca672d..cb6ce42f8e7 100644 --- a/drivers/pnp/core.c +++ b/drivers/pnp/core.c @@ -70,7 +70,7 @@ int pnp_register_protocol(struct pnp_protocol *protocol) spin_unlock(&pnp_lock); protocol->number = nodenum; - sprintf(protocol->dev.bus_id, "pnp%d", nodenum); + dev_set_name(&protocol->dev, "pnp%d", nodenum); return device_register(&protocol->dev); } @@ -99,37 +99,76 @@ static void pnp_free_ids(struct pnp_dev *dev) } } +void pnp_free_resource(struct pnp_resource *pnp_res) +{ + list_del(&pnp_res->list); + kfree(pnp_res); +} + +void pnp_free_resources(struct pnp_dev *dev) +{ + struct pnp_resource *pnp_res, *tmp; + + list_for_each_entry_safe(pnp_res, tmp, &dev->resources, list) { + pnp_free_resource(pnp_res); + } +} + static void pnp_release_device(struct device *dmdev) { struct pnp_dev *dev = to_pnp_dev(dmdev); - pnp_free_option(dev->independent); - pnp_free_option(dev->dependent); pnp_free_ids(dev); + pnp_free_resources(dev); + pnp_free_options(dev); kfree(dev); } -int __pnp_add_device(struct pnp_dev *dev) +struct pnp_dev *pnp_alloc_dev(struct pnp_protocol *protocol, int id, + const char *pnpid) { - int ret; + struct pnp_dev *dev; + struct pnp_id *dev_id; - pnp_fixup_device(dev); + dev = kzalloc(sizeof(struct pnp_dev), GFP_KERNEL); + if (!dev) + return NULL; + + INIT_LIST_HEAD(&dev->resources); + INIT_LIST_HEAD(&dev->options); + dev->protocol = protocol; + dev->number = id; + dev->dma_mask = DMA_BIT_MASK(24); + + dev->dev.parent = &dev->protocol->dev; dev->dev.bus = &pnp_bus_type; dev->dev.dma_mask = &dev->dma_mask; - dev->dma_mask = dev->dev.coherent_dma_mask = DMA_24BIT_MASK; + dev->dev.coherent_dma_mask = dev->dma_mask; dev->dev.release = &pnp_release_device; + + dev_set_name(&dev->dev, "%02x:%02x", dev->protocol->number, dev->number); + + dev_id = pnp_add_id(dev, pnpid); + if (!dev_id) { + kfree(dev); + return NULL; + } + + return dev; +} + +int __pnp_add_device(struct pnp_dev *dev) +{ + pnp_fixup_device(dev); dev->status = PNP_READY; spin_lock(&pnp_lock); list_add_tail(&dev->global_list, &pnp_global); list_add_tail(&dev->protocol_list, &dev->protocol->devices); spin_unlock(&pnp_lock); - - ret = device_register(&dev->dev); - if (ret) - return ret; - - pnp_interface_attach_device(dev); - return 0; + if (dev->protocol->can_wakeup) + device_set_wakeup_capable(&dev->dev, + dev->protocol->can_wakeup(dev)); + return device_register(&dev->dev); } /* @@ -141,28 +180,24 @@ int __pnp_add_device(struct pnp_dev *dev) int pnp_add_device(struct pnp_dev *dev) { int ret; + char buf[128]; + int len = 0; + struct pnp_id *id; if (dev->card) return -EINVAL; - dev->dev.parent = &dev->protocol->dev; - sprintf(dev->dev.bus_id, "%02x:%02x", dev->protocol->number, - dev->number); ret = __pnp_add_device(dev); if (ret) return ret; -#ifdef CONFIG_PNP_DEBUG - { - struct pnp_id *id; + buf[0] = '\0'; + for (id = dev->id; id; id = id->next) + len += scnprintf(buf + len, sizeof(buf) - len, " %s", id->id); - dev_printk(KERN_DEBUG, &dev->dev, "%s device, IDs", - dev->protocol->name); - for (id = dev->id; id; id = id->next) - printk(" %s", id->id); - printk(" (%s)\n", dev->active ? "active" : "disabled"); - } -#endif + dev_printk(KERN_DEBUG, &dev->dev, "%s device, IDs%s (%s)\n", + dev->protocol->name, buf, + dev->active ? "active" : "disabled"); return 0; } @@ -177,8 +212,13 @@ void __pnp_remove_device(struct pnp_dev *dev) static int __init pnp_init(void) { - printk(KERN_INFO "Linux Plug and Play Support v0.97 (c) Adam Belay\n"); return bus_register(&pnp_bus_type); } subsys_initcall(pnp_init); + +int pnp_debug; + +#if defined(CONFIG_PNP_DEBUG_MESSAGES) +module_param_named(debug, pnp_debug, int, 0644); +#endif diff --git a/drivers/pnp/driver.c b/drivers/pnp/driver.c index 12a1645a2e4..f748cc8cbb0 100644 --- a/drivers/pnp/driver.c +++ b/drivers/pnp/driver.c @@ -114,7 +114,6 @@ static int pnp_device_probe(struct device *dev) } else goto fail; - dev_dbg(dev, "driver attached\n"); return error; fail: @@ -136,6 +135,15 @@ static int pnp_device_remove(struct device *dev) return 0; } +static void pnp_device_shutdown(struct device *dev) +{ + struct pnp_dev *pnp_dev = to_pnp_dev(dev); + struct pnp_driver *drv = pnp_dev->driver; + + if (drv && drv->shutdown) + drv->shutdown(pnp_dev); +} + static int pnp_bus_match(struct device *dev, struct device_driver *drv) { struct pnp_dev *pnp_dev = to_pnp_dev(dev); @@ -146,7 +154,7 @@ static int pnp_bus_match(struct device *dev, struct device_driver *drv) return 1; } -static int pnp_bus_suspend(struct device *dev, pm_message_t state) +static int __pnp_bus_suspend(struct device *dev, pm_message_t state) { struct pnp_dev *pnp_dev = to_pnp_dev(dev); struct pnp_driver *pnp_drv = pnp_dev->driver; @@ -155,6 +163,13 @@ static int pnp_bus_suspend(struct device *dev, pm_message_t state) if (!pnp_drv) return 0; + if (pnp_drv->driver.pm && pnp_drv->driver.pm->suspend) { + error = pnp_drv->driver.pm->suspend(dev); + suspend_report_result(pnp_drv->driver.pm->suspend, error); + if (error) + return error; + } + if (pnp_drv->suspend) { error = pnp_drv->suspend(pnp_dev, state); if (error) @@ -167,11 +182,26 @@ static int pnp_bus_suspend(struct device *dev, pm_message_t state) return error; } - if (pnp_dev->protocol && pnp_dev->protocol->suspend) + if (pnp_dev->protocol->suspend) pnp_dev->protocol->suspend(pnp_dev, state); return 0; } +static int pnp_bus_suspend(struct device *dev) +{ + return __pnp_bus_suspend(dev, PMSG_SUSPEND); +} + +static int pnp_bus_freeze(struct device *dev) +{ + return __pnp_bus_suspend(dev, PMSG_FREEZE); +} + +static int pnp_bus_poweroff(struct device *dev) +{ + return __pnp_bus_suspend(dev, PMSG_HIBERNATE); +} + static int pnp_bus_resume(struct device *dev) { struct pnp_dev *pnp_dev = to_pnp_dev(dev); @@ -181,8 +211,11 @@ static int pnp_bus_resume(struct device *dev) if (!pnp_drv) return 0; - if (pnp_dev->protocol && pnp_dev->protocol->resume) - pnp_dev->protocol->resume(pnp_dev); + if (pnp_dev->protocol->resume) { + error = pnp_dev->protocol->resume(pnp_dev); + if (error) + return error; + } if (pnp_can_write(pnp_dev)) { error = pnp_start_dev(pnp_dev); @@ -190,6 +223,12 @@ static int pnp_bus_resume(struct device *dev) return error; } + if (pnp_drv->driver.pm && pnp_drv->driver.pm->resume) { + error = pnp_drv->driver.pm->resume(dev); + if (error) + return error; + } + if (pnp_drv->resume) { error = pnp_drv->resume(pnp_dev); if (error) @@ -199,19 +238,29 @@ static int pnp_bus_resume(struct device *dev) return 0; } +static const struct dev_pm_ops pnp_bus_dev_pm_ops = { + /* Suspend callbacks */ + .suspend = pnp_bus_suspend, + .resume = pnp_bus_resume, + /* Hibernate callbacks */ + .freeze = pnp_bus_freeze, + .thaw = pnp_bus_resume, + .poweroff = pnp_bus_poweroff, + .restore = pnp_bus_resume, +}; + struct bus_type pnp_bus_type = { .name = "pnp", .match = pnp_bus_match, .probe = pnp_device_probe, .remove = pnp_device_remove, - .suspend = pnp_bus_suspend, - .resume = pnp_bus_resume, + .shutdown = pnp_device_shutdown, + .pm = &pnp_bus_dev_pm_ops, + .dev_groups = pnp_dev_groups, }; int pnp_register_driver(struct pnp_driver *drv) { - pnp_dbg("the driver '%s' has been registered", drv->name); - drv->driver.name = drv->name; drv->driver.bus = &pnp_bus_type; @@ -221,27 +270,40 @@ int pnp_register_driver(struct pnp_driver *drv) void pnp_unregister_driver(struct pnp_driver *drv) { driver_unregister(&drv->driver); - pnp_dbg("the driver '%s' has been unregistered", drv->name); } /** * pnp_add_id - adds an EISA id to the specified device - * @id: pointer to a pnp_id structure * @dev: pointer to the desired device + * @id: pointer to an EISA id string */ -int pnp_add_id(struct pnp_id *id, struct pnp_dev *dev) +struct pnp_id *pnp_add_id(struct pnp_dev *dev, const char *id) { - struct pnp_id *ptr; + struct pnp_id *dev_id, *ptr; - id->next = NULL; + dev_id = kzalloc(sizeof(struct pnp_id), GFP_KERNEL); + if (!dev_id) + return NULL; + + dev_id->id[0] = id[0]; + dev_id->id[1] = id[1]; + dev_id->id[2] = id[2]; + dev_id->id[3] = tolower(id[3]); + dev_id->id[4] = tolower(id[4]); + dev_id->id[5] = tolower(id[5]); + dev_id->id[6] = tolower(id[6]); + dev_id->id[7] = '\0'; + + dev_id->next = NULL; ptr = dev->id; while (ptr && ptr->next) ptr = ptr->next; if (ptr) - ptr->next = id; + ptr->next = dev_id; else - dev->id = id; - return 0; + dev->id = dev_id; + + return dev_id; } EXPORT_SYMBOL(pnp_register_driver); diff --git a/drivers/pnp/interface.c b/drivers/pnp/interface.c index 982658477a5..e6c403be09a 100644 --- a/drivers/pnp/interface.c +++ b/drivers/pnp/interface.c @@ -3,6 +3,8 @@ * * Some code, especially possible resource dumping is based on isapnp_proc.c (c) Jaroslav Kysela <perex@perex.cz> * Copyright 2002 Adam Belay <ambx1@neo.rr.com> + * Copyright (C) 2008 Hewlett-Packard Development Company, L.P. + * Bjorn Helgaas <bjorn.helgaas@hp.com> */ #include <linux/pnp.h> @@ -10,7 +12,6 @@ #include <linux/errno.h> #include <linux/list.h> #include <linux/types.h> -#include <linux/pnp.h> #include <linux/stat.h> #include <linux/ctype.h> #include <linux/slab.h> @@ -53,11 +54,13 @@ static int pnp_printf(pnp_info_buffer_t * buffer, char *fmt, ...) static void pnp_print_port(pnp_info_buffer_t * buffer, char *space, struct pnp_port *port) { - pnp_printf(buffer, - "%sport 0x%x-0x%x, align 0x%x, size 0x%x, %i-bit address decoding\n", - space, port->min, port->max, - port->align ? (port->align - 1) : 0, port->size, - port->flags & PNP_PORT_FLAG_16BITADDR ? 16 : 10); + pnp_printf(buffer, "%sport %#llx-%#llx, align %#llx, size %#llx, " + "%i-bit address decoding\n", space, + (unsigned long long) port->min, + (unsigned long long) port->max, + port->align ? ((unsigned long long) port->align - 1) : 0, + (unsigned long long) port->size, + port->flags & IORESOURCE_IO_16BIT_ADDR ? 16 : 10); } static void pnp_print_irq(pnp_info_buffer_t * buffer, char *space, @@ -67,7 +70,7 @@ static void pnp_print_irq(pnp_info_buffer_t * buffer, char *space, pnp_printf(buffer, "%sirq ", space); for (i = 0; i < PNP_IRQ_NR; i++) - if (test_bit(i, irq->map)) { + if (test_bit(i, irq->map.bits)) { if (!first) { pnp_printf(buffer, ","); } else { @@ -78,7 +81,7 @@ static void pnp_print_irq(pnp_info_buffer_t * buffer, char *space, else pnp_printf(buffer, "%i", i); } - if (bitmap_empty(irq->map, PNP_IRQ_NR)) + if (bitmap_empty(irq->map.bits, PNP_IRQ_NR)) pnp_printf(buffer, "<none>"); if (irq->flags & IORESOURCE_IRQ_HIGHEDGE) pnp_printf(buffer, " High-Edge"); @@ -88,6 +91,8 @@ static void pnp_print_irq(pnp_info_buffer_t * buffer, char *space, pnp_printf(buffer, " High-Level"); if (irq->flags & IORESOURCE_IRQ_LOWLEVEL) pnp_printf(buffer, " Low-Level"); + if (irq->flags & IORESOURCE_IRQ_OPTIONAL) + pnp_printf(buffer, " (optional)"); pnp_printf(buffer, "\n"); } @@ -148,8 +153,11 @@ static void pnp_print_mem(pnp_info_buffer_t * buffer, char *space, { char *s; - pnp_printf(buffer, "%sMemory 0x%x-0x%x, align 0x%x, size 0x%x", - space, mem->min, mem->max, mem->align, mem->size); + pnp_printf(buffer, "%sMemory %#llx-%#llx, align %#llx, size %#llx", + space, (unsigned long long) mem->min, + (unsigned long long) mem->max, + (unsigned long long) mem->align, + (unsigned long long) mem->size); if (mem->flags & IORESOURCE_MEM_WRITEABLE) pnp_printf(buffer, ", writeable"); if (mem->flags & IORESOURCE_MEM_CACHEABLE) @@ -177,150 +185,155 @@ static void pnp_print_mem(pnp_info_buffer_t * buffer, char *space, } static void pnp_print_option(pnp_info_buffer_t * buffer, char *space, - struct pnp_option *option, int dep) + struct pnp_option *option) { - char *s; - struct pnp_port *port; - struct pnp_irq *irq; - struct pnp_dma *dma; - struct pnp_mem *mem; - - if (dep) { - switch (option->priority) { - case PNP_RES_PRIORITY_PREFERRED: - s = "preferred"; - break; - case PNP_RES_PRIORITY_ACCEPTABLE: - s = "acceptable"; - break; - case PNP_RES_PRIORITY_FUNCTIONAL: - s = "functional"; - break; - default: - s = "invalid"; - } - pnp_printf(buffer, "Dependent: %02i - Priority %s\n", dep, s); + switch (option->type) { + case IORESOURCE_IO: + pnp_print_port(buffer, space, &option->u.port); + break; + case IORESOURCE_MEM: + pnp_print_mem(buffer, space, &option->u.mem); + break; + case IORESOURCE_IRQ: + pnp_print_irq(buffer, space, &option->u.irq); + break; + case IORESOURCE_DMA: + pnp_print_dma(buffer, space, &option->u.dma); + break; } - - for (port = option->port; port; port = port->next) - pnp_print_port(buffer, space, port); - for (irq = option->irq; irq; irq = irq->next) - pnp_print_irq(buffer, space, irq); - for (dma = option->dma; dma; dma = dma->next) - pnp_print_dma(buffer, space, dma); - for (mem = option->mem; mem; mem = mem->next) - pnp_print_mem(buffer, space, mem); } -static ssize_t pnp_show_options(struct device *dmdev, - struct device_attribute *attr, char *buf) +static ssize_t options_show(struct device *dmdev, struct device_attribute *attr, + char *buf) { struct pnp_dev *dev = to_pnp_dev(dmdev); - struct pnp_option *independent = dev->independent; - struct pnp_option *dependent = dev->dependent; - int ret, dep = 1; + pnp_info_buffer_t *buffer; + struct pnp_option *option; + int ret, dep = 0, set = 0; + char *indent; - pnp_info_buffer_t *buffer = (pnp_info_buffer_t *) - pnp_alloc(sizeof(pnp_info_buffer_t)); + buffer = pnp_alloc(sizeof(pnp_info_buffer_t)); if (!buffer) return -ENOMEM; buffer->len = PAGE_SIZE; buffer->buffer = buf; buffer->curr = buffer->buffer; - if (independent) - pnp_print_option(buffer, "", independent, 0); - while (dependent) { - pnp_print_option(buffer, " ", dependent, dep); - dependent = dependent->next; - dep++; + list_for_each_entry(option, &dev->options, list) { + if (pnp_option_is_dependent(option)) { + indent = " "; + if (!dep || pnp_option_set(option) != set) { + set = pnp_option_set(option); + dep = 1; + pnp_printf(buffer, "Dependent: %02i - " + "Priority %s\n", set, + pnp_option_priority_name(option)); + } + } else { + dep = 0; + indent = ""; + } + pnp_print_option(buffer, indent, option); } + ret = (buffer->curr - buf); kfree(buffer); return ret; } +static DEVICE_ATTR_RO(options); -static DEVICE_ATTR(options, S_IRUGO, pnp_show_options, NULL); - -static ssize_t pnp_show_current_resources(struct device *dmdev, - struct device_attribute *attr, - char *buf) +static ssize_t resources_show(struct device *dmdev, + struct device_attribute *attr, char *buf) { struct pnp_dev *dev = to_pnp_dev(dmdev); - int i, ret; pnp_info_buffer_t *buffer; + struct pnp_resource *pnp_res; + struct resource *res; + int ret; if (!dev) return -EINVAL; - buffer = (pnp_info_buffer_t *) pnp_alloc(sizeof(pnp_info_buffer_t)); + buffer = pnp_alloc(sizeof(pnp_info_buffer_t)); if (!buffer) return -ENOMEM; + buffer->len = PAGE_SIZE; buffer->buffer = buf; buffer->curr = buffer->buffer; - pnp_printf(buffer, "state = "); - if (dev->active) - pnp_printf(buffer, "active\n"); - else - pnp_printf(buffer, "disabled\n"); - - for (i = 0; i < PNP_MAX_PORT; i++) { - if (pnp_port_valid(dev, i)) { - pnp_printf(buffer, "io"); - if (pnp_port_flags(dev, i) & IORESOURCE_DISABLED) - pnp_printf(buffer, " disabled\n"); - else - pnp_printf(buffer, " 0x%llx-0x%llx\n", - (unsigned long long) - pnp_port_start(dev, i), - (unsigned long long)pnp_port_end(dev, - i)); - } - } - for (i = 0; i < PNP_MAX_MEM; i++) { - if (pnp_mem_valid(dev, i)) { - pnp_printf(buffer, "mem"); - if (pnp_mem_flags(dev, i) & IORESOURCE_DISABLED) - pnp_printf(buffer, " disabled\n"); - else - pnp_printf(buffer, " 0x%llx-0x%llx\n", - (unsigned long long) - pnp_mem_start(dev, i), - (unsigned long long)pnp_mem_end(dev, - i)); - } - } - for (i = 0; i < PNP_MAX_IRQ; i++) { - if (pnp_irq_valid(dev, i)) { - pnp_printf(buffer, "irq"); - if (pnp_irq_flags(dev, i) & IORESOURCE_DISABLED) - pnp_printf(buffer, " disabled\n"); - else - pnp_printf(buffer, " %lld\n", - (unsigned long long)pnp_irq(dev, i)); + pnp_printf(buffer, "state = %s\n", dev->active ? "active" : "disabled"); + + list_for_each_entry(pnp_res, &dev->resources, list) { + res = &pnp_res->res; + + pnp_printf(buffer, pnp_resource_type_name(res)); + + if (res->flags & IORESOURCE_DISABLED) { + pnp_printf(buffer, " disabled\n"); + continue; } - } - for (i = 0; i < PNP_MAX_DMA; i++) { - if (pnp_dma_valid(dev, i)) { - pnp_printf(buffer, "dma"); - if (pnp_dma_flags(dev, i) & IORESOURCE_DISABLED) - pnp_printf(buffer, " disabled\n"); - else - pnp_printf(buffer, " %lld\n", - (unsigned long long)pnp_dma(dev, i)); + + switch (pnp_resource_type(res)) { + case IORESOURCE_IO: + case IORESOURCE_MEM: + case IORESOURCE_BUS: + pnp_printf(buffer, " %#llx-%#llx%s\n", + (unsigned long long) res->start, + (unsigned long long) res->end, + res->flags & IORESOURCE_WINDOW ? + " window" : ""); + break; + case IORESOURCE_IRQ: + case IORESOURCE_DMA: + pnp_printf(buffer, " %lld\n", + (unsigned long long) res->start); + break; } } + ret = (buffer->curr - buf); kfree(buffer); return ret; } -static ssize_t -pnp_set_current_resources(struct device *dmdev, struct device_attribute *attr, - const char *ubuf, size_t count) +static char *pnp_get_resource_value(char *buf, + unsigned long type, + resource_size_t *start, + resource_size_t *end, + unsigned long *flags) +{ + if (start) + *start = 0; + if (end) + *end = 0; + if (flags) + *flags = 0; + + /* TBD: allow for disabled resources */ + + buf = skip_spaces(buf); + if (start) { + *start = simple_strtoull(buf, &buf, 0); + if (end) { + buf = skip_spaces(buf); + if (*buf == '-') { + buf = skip_spaces(buf + 1); + *end = simple_strtoull(buf, &buf, 0); + } else + *end = *start; + } + } + + /* TBD: allow for additional flags, e.g., IORESOURCE_WINDOW */ + + return buf; +} + +static ssize_t resources_store(struct device *dmdev, + struct device_attribute *attr, const char *ubuf, + size_t count) { struct pnp_dev *dev = to_pnp_dev(dmdev); char *buf = (void *)ubuf; @@ -332,8 +345,7 @@ pnp_set_current_resources(struct device *dmdev, struct device_attribute *attr, goto done; } - while (isspace(*buf)) - ++buf; + buf = skip_spaces(buf); if (!strnicmp(buf, "disable", 7)) { retval = pnp_disable_dev(dev); goto done; @@ -351,110 +363,67 @@ pnp_set_current_resources(struct device *dmdev, struct device_attribute *attr, if (!strnicmp(buf, "auto", 4)) { if (dev->active) goto done; - pnp_init_resource_table(&dev->res); + pnp_init_resources(dev); retval = pnp_auto_config_dev(dev); goto done; } if (!strnicmp(buf, "clear", 5)) { if (dev->active) goto done; - pnp_init_resource_table(&dev->res); + pnp_init_resources(dev); goto done; } if (!strnicmp(buf, "get", 3)) { mutex_lock(&pnp_res_mutex); if (pnp_can_read(dev)) - dev->protocol->get(dev, &dev->res); + dev->protocol->get(dev); mutex_unlock(&pnp_res_mutex); goto done; } if (!strnicmp(buf, "set", 3)) { - int nport = 0, nmem = 0, nirq = 0, ndma = 0; + resource_size_t start; + resource_size_t end; + unsigned long flags; + if (dev->active) goto done; buf += 3; - pnp_init_resource_table(&dev->res); + pnp_init_resources(dev); mutex_lock(&pnp_res_mutex); while (1) { - while (isspace(*buf)) - ++buf; + buf = skip_spaces(buf); if (!strnicmp(buf, "io", 2)) { - buf += 2; - while (isspace(*buf)) - ++buf; - dev->res.port_resource[nport].start = - simple_strtoul(buf, &buf, 0); - while (isspace(*buf)) - ++buf; - if (*buf == '-') { - buf += 1; - while (isspace(*buf)) - ++buf; - dev->res.port_resource[nport].end = - simple_strtoul(buf, &buf, 0); - } else - dev->res.port_resource[nport].end = - dev->res.port_resource[nport].start; - dev->res.port_resource[nport].flags = - IORESOURCE_IO; - nport++; - if (nport >= PNP_MAX_PORT) - break; - continue; - } - if (!strnicmp(buf, "mem", 3)) { - buf += 3; - while (isspace(*buf)) - ++buf; - dev->res.mem_resource[nmem].start = - simple_strtoul(buf, &buf, 0); - while (isspace(*buf)) - ++buf; - if (*buf == '-') { - buf += 1; - while (isspace(*buf)) - ++buf; - dev->res.mem_resource[nmem].end = - simple_strtoul(buf, &buf, 0); - } else - dev->res.mem_resource[nmem].end = - dev->res.mem_resource[nmem].start; - dev->res.mem_resource[nmem].flags = - IORESOURCE_MEM; - nmem++; - if (nmem >= PNP_MAX_MEM) - break; - continue; - } - if (!strnicmp(buf, "irq", 3)) { - buf += 3; - while (isspace(*buf)) - ++buf; - dev->res.irq_resource[nirq].start = - dev->res.irq_resource[nirq].end = - simple_strtoul(buf, &buf, 0); - dev->res.irq_resource[nirq].flags = - IORESOURCE_IRQ; - nirq++; - if (nirq >= PNP_MAX_IRQ) - break; - continue; - } - if (!strnicmp(buf, "dma", 3)) { - buf += 3; - while (isspace(*buf)) - ++buf; - dev->res.dma_resource[ndma].start = - dev->res.dma_resource[ndma].end = - simple_strtoul(buf, &buf, 0); - dev->res.dma_resource[ndma].flags = - IORESOURCE_DMA; - ndma++; - if (ndma >= PNP_MAX_DMA) - break; - continue; - } - break; + buf = pnp_get_resource_value(buf + 2, + IORESOURCE_IO, + &start, &end, + &flags); + pnp_add_io_resource(dev, start, end, flags); + } else if (!strnicmp(buf, "mem", 3)) { + buf = pnp_get_resource_value(buf + 3, + IORESOURCE_MEM, + &start, &end, + &flags); + pnp_add_mem_resource(dev, start, end, flags); + } else if (!strnicmp(buf, "irq", 3)) { + buf = pnp_get_resource_value(buf + 3, + IORESOURCE_IRQ, + &start, NULL, + &flags); + pnp_add_irq_resource(dev, start, flags); + } else if (!strnicmp(buf, "dma", 3)) { + buf = pnp_get_resource_value(buf + 3, + IORESOURCE_DMA, + &start, NULL, + &flags); + pnp_add_dma_resource(dev, start, flags); + } else if (!strnicmp(buf, "bus", 3)) { + buf = pnp_get_resource_value(buf + 3, + IORESOURCE_BUS, + &start, &end, + NULL); + pnp_add_bus_resource(dev, start, end); + } else + break; } mutex_unlock(&pnp_res_mutex); goto done; @@ -465,12 +434,10 @@ done: return retval; return count; } +static DEVICE_ATTR_RW(resources); -static DEVICE_ATTR(resources, S_IRUGO | S_IWUSR, - pnp_show_current_resources, pnp_set_current_resources); - -static ssize_t pnp_show_current_ids(struct device *dmdev, - struct device_attribute *attr, char *buf) +static ssize_t id_show(struct device *dmdev, struct device_attribute *attr, + char *buf) { char *str = buf; struct pnp_dev *dev = to_pnp_dev(dmdev); @@ -482,28 +449,20 @@ static ssize_t pnp_show_current_ids(struct device *dmdev, } return (str - buf); } +static DEVICE_ATTR_RO(id); -static DEVICE_ATTR(id, S_IRUGO, pnp_show_current_ids, NULL); +static struct attribute *pnp_dev_attrs[] = { + &dev_attr_resources.attr, + &dev_attr_options.attr, + &dev_attr_id.attr, + NULL, +}; -int pnp_interface_attach_device(struct pnp_dev *dev) -{ - int rc = device_create_file(&dev->dev, &dev_attr_options); - - if (rc) - goto err; - rc = device_create_file(&dev->dev, &dev_attr_resources); - if (rc) - goto err_opt; - rc = device_create_file(&dev->dev, &dev_attr_id); - if (rc) - goto err_res; - - return 0; - -err_res: - device_remove_file(&dev->dev, &dev_attr_resources); -err_opt: - device_remove_file(&dev->dev, &dev_attr_options); -err: - return rc; -} +static const struct attribute_group pnp_dev_group = { + .attrs = pnp_dev_attrs, +}; + +const struct attribute_group *pnp_dev_groups[] = { + &pnp_dev_group, + NULL, +}; diff --git a/drivers/pnp/isapnp/Makefile b/drivers/pnp/isapnp/Makefile index cac18bbfb81..6e607aa33aa 100644 --- a/drivers/pnp/isapnp/Makefile +++ b/drivers/pnp/isapnp/Makefile @@ -1,7 +1,7 @@ # # Makefile for the kernel ISAPNP driver. # +obj-y += pnp.o +pnp-y := core.o compat.o -isapnp-proc-$(CONFIG_PROC_FS) = proc.o - -obj-y := core.o compat.o $(isapnp-proc-y) +pnp-$(CONFIG_PROC_FS) += proc.o diff --git a/drivers/pnp/isapnp/core.c b/drivers/pnp/isapnp/core.c index 257f5d827d8..cf88f9b6244 100644 --- a/drivers/pnp/isapnp/core.c +++ b/drivers/pnp/isapnp/core.c @@ -37,13 +37,14 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/errno.h> -#include <linux/slab.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/isapnp.h> #include <linux/mutex.h> #include <asm/io.h> +#include "../base.h" + #if 0 #define ISAPNP_REGION_OK #endif @@ -88,6 +89,14 @@ MODULE_LICENSE("GPL"); #define _LTAG_MEM32RANGE 0x85 #define _LTAG_FIXEDMEM32RANGE 0x86 +/* Logical device control and configuration registers */ + +#define ISAPNP_CFG_ACTIVATE 0x30 /* byte */ +#define ISAPNP_CFG_MEM 0x40 /* 4 * dword */ +#define ISAPNP_CFG_PORT 0x60 /* 8 * word */ +#define ISAPNP_CFG_IRQ 0x70 /* 2 * word */ +#define ISAPNP_CFG_DMA 0x74 /* 2 * byte */ + /* * Sizes of ISAPNP logical device configuration register sets. * See PNP-ISA-v1.0a.pdf, Appendix A. @@ -370,10 +379,6 @@ static int __init isapnp_read_tag(unsigned char *type, unsigned short *size) *type = (tag >> 3) & 0x0f; *size = tag & 0x07; } -#if 0 - printk(KERN_DEBUG "tag = 0x%x, type = 0x%x, size = %i\n", tag, *type, - *size); -#endif if (*type == 0xff && *size == 0xffff) /* probably invalid data */ return -1; return 0; @@ -388,28 +393,6 @@ static void __init isapnp_skip_bytes(int count) } /* - * Parse EISA id. - */ -static void isapnp_parse_id(struct pnp_dev *dev, unsigned short vendor, - unsigned short device) -{ - struct pnp_id *id; - - if (!dev) - return; - id = kzalloc(sizeof(struct pnp_id), GFP_KERNEL); - if (!id) - return; - sprintf(id->id, "%c%c%c%x%x%x%x", - 'A' + ((vendor >> 2) & 0x3f) - 1, - 'A' + (((vendor & 3) << 3) | ((vendor >> 13) & 7)) - 1, - 'A' + ((vendor >> 8) & 0x1f) - 1, - (device >> 4) & 0x0f, - device & 0x0f, (device >> 12) & 0x0f, (device >> 8) & 0x0f); - pnp_add_id(id, dev); -} - -/* * Parse logical device tag. */ static struct pnp_dev *__init isapnp_parse_device(struct pnp_card *card, @@ -417,171 +400,159 @@ static struct pnp_dev *__init isapnp_parse_device(struct pnp_card *card, { unsigned char tmp[6]; struct pnp_dev *dev; + u32 eisa_id; + char id[8]; isapnp_peek(tmp, size); - dev = kzalloc(sizeof(struct pnp_dev), GFP_KERNEL); + eisa_id = tmp[0] | tmp[1] << 8 | tmp[2] << 16 | tmp[3] << 24; + pnp_eisa_id_to_string(eisa_id, id); + + dev = pnp_alloc_dev(&isapnp_protocol, number, id); if (!dev) return NULL; - dev->number = number; - isapnp_parse_id(dev, (tmp[1] << 8) | tmp[0], (tmp[3] << 8) | tmp[2]); - dev->regs = tmp[4]; + dev->card = card; - if (size > 5) - dev->regs |= tmp[5] << 8; - dev->protocol = &isapnp_protocol; dev->capabilities |= PNP_CONFIGURABLE; dev->capabilities |= PNP_READ; dev->capabilities |= PNP_WRITE; dev->capabilities |= PNP_DISABLE; - pnp_init_resource_table(&dev->res); + pnp_init_resources(dev); return dev; } /* * Add IRQ resource to resources list. */ -static void __init isapnp_parse_irq_resource(struct pnp_option *option, +static void __init isapnp_parse_irq_resource(struct pnp_dev *dev, + unsigned int option_flags, int size) { unsigned char tmp[3]; - struct pnp_irq *irq; unsigned long bits; + pnp_irq_mask_t map; + unsigned char flags = IORESOURCE_IRQ_HIGHEDGE; isapnp_peek(tmp, size); - irq = kzalloc(sizeof(struct pnp_irq), GFP_KERNEL); - if (!irq) - return; bits = (tmp[1] << 8) | tmp[0]; - bitmap_copy(irq->map, &bits, 16); + + bitmap_zero(map.bits, PNP_IRQ_NR); + bitmap_copy(map.bits, &bits, 16); + if (size > 2) - irq->flags = tmp[2]; - else - irq->flags = IORESOURCE_IRQ_HIGHEDGE; - pnp_register_irq_resource(option, irq); + flags = tmp[2]; + + pnp_register_irq_resource(dev, option_flags, &map, flags); } /* * Add DMA resource to resources list. */ -static void __init isapnp_parse_dma_resource(struct pnp_option *option, +static void __init isapnp_parse_dma_resource(struct pnp_dev *dev, + unsigned int option_flags, int size) { unsigned char tmp[2]; - struct pnp_dma *dma; isapnp_peek(tmp, size); - dma = kzalloc(sizeof(struct pnp_dma), GFP_KERNEL); - if (!dma) - return; - dma->map = tmp[0]; - dma->flags = tmp[1]; - pnp_register_dma_resource(option, dma); + pnp_register_dma_resource(dev, option_flags, tmp[0], tmp[1]); } /* * Add port resource to resources list. */ -static void __init isapnp_parse_port_resource(struct pnp_option *option, +static void __init isapnp_parse_port_resource(struct pnp_dev *dev, + unsigned int option_flags, int size) { unsigned char tmp[7]; - struct pnp_port *port; + resource_size_t min, max, align, len; + unsigned char flags; isapnp_peek(tmp, size); - port = kzalloc(sizeof(struct pnp_port), GFP_KERNEL); - if (!port) - return; - port->min = (tmp[2] << 8) | tmp[1]; - port->max = (tmp[4] << 8) | tmp[3]; - port->align = tmp[5]; - port->size = tmp[6]; - port->flags = tmp[0] ? PNP_PORT_FLAG_16BITADDR : 0; - pnp_register_port_resource(option, port); + min = (tmp[2] << 8) | tmp[1]; + max = (tmp[4] << 8) | tmp[3]; + align = tmp[5]; + len = tmp[6]; + flags = tmp[0] ? IORESOURCE_IO_16BIT_ADDR : 0; + pnp_register_port_resource(dev, option_flags, + min, max, align, len, flags); } /* * Add fixed port resource to resources list. */ -static void __init isapnp_parse_fixed_port_resource(struct pnp_option *option, +static void __init isapnp_parse_fixed_port_resource(struct pnp_dev *dev, + unsigned int option_flags, int size) { unsigned char tmp[3]; - struct pnp_port *port; + resource_size_t base, len; isapnp_peek(tmp, size); - port = kzalloc(sizeof(struct pnp_port), GFP_KERNEL); - if (!port) - return; - port->min = port->max = (tmp[1] << 8) | tmp[0]; - port->size = tmp[2]; - port->align = 0; - port->flags = PNP_PORT_FLAG_FIXED; - pnp_register_port_resource(option, port); + base = (tmp[1] << 8) | tmp[0]; + len = tmp[2]; + pnp_register_port_resource(dev, option_flags, base, base, 0, len, + IORESOURCE_IO_FIXED); } /* * Add memory resource to resources list. */ -static void __init isapnp_parse_mem_resource(struct pnp_option *option, +static void __init isapnp_parse_mem_resource(struct pnp_dev *dev, + unsigned int option_flags, int size) { unsigned char tmp[9]; - struct pnp_mem *mem; + resource_size_t min, max, align, len; + unsigned char flags; isapnp_peek(tmp, size); - mem = kzalloc(sizeof(struct pnp_mem), GFP_KERNEL); - if (!mem) - return; - mem->min = ((tmp[2] << 8) | tmp[1]) << 8; - mem->max = ((tmp[4] << 8) | tmp[3]) << 8; - mem->align = (tmp[6] << 8) | tmp[5]; - mem->size = ((tmp[8] << 8) | tmp[7]) << 8; - mem->flags = tmp[0]; - pnp_register_mem_resource(option, mem); + min = ((tmp[2] << 8) | tmp[1]) << 8; + max = ((tmp[4] << 8) | tmp[3]) << 8; + align = (tmp[6] << 8) | tmp[5]; + len = ((tmp[8] << 8) | tmp[7]) << 8; + flags = tmp[0]; + pnp_register_mem_resource(dev, option_flags, + min, max, align, len, flags); } /* * Add 32-bit memory resource to resources list. */ -static void __init isapnp_parse_mem32_resource(struct pnp_option *option, +static void __init isapnp_parse_mem32_resource(struct pnp_dev *dev, + unsigned int option_flags, int size) { unsigned char tmp[17]; - struct pnp_mem *mem; + resource_size_t min, max, align, len; + unsigned char flags; isapnp_peek(tmp, size); - mem = kzalloc(sizeof(struct pnp_mem), GFP_KERNEL); - if (!mem) - return; - mem->min = (tmp[4] << 24) | (tmp[3] << 16) | (tmp[2] << 8) | tmp[1]; - mem->max = (tmp[8] << 24) | (tmp[7] << 16) | (tmp[6] << 8) | tmp[5]; - mem->align = - (tmp[12] << 24) | (tmp[11] << 16) | (tmp[10] << 8) | tmp[9]; - mem->size = - (tmp[16] << 24) | (tmp[15] << 16) | (tmp[14] << 8) | tmp[13]; - mem->flags = tmp[0]; - pnp_register_mem_resource(option, mem); + min = (tmp[4] << 24) | (tmp[3] << 16) | (tmp[2] << 8) | tmp[1]; + max = (tmp[8] << 24) | (tmp[7] << 16) | (tmp[6] << 8) | tmp[5]; + align = (tmp[12] << 24) | (tmp[11] << 16) | (tmp[10] << 8) | tmp[9]; + len = (tmp[16] << 24) | (tmp[15] << 16) | (tmp[14] << 8) | tmp[13]; + flags = tmp[0]; + pnp_register_mem_resource(dev, option_flags, + min, max, align, len, flags); } /* * Add 32-bit fixed memory resource to resources list. */ -static void __init isapnp_parse_fixed_mem32_resource(struct pnp_option *option, +static void __init isapnp_parse_fixed_mem32_resource(struct pnp_dev *dev, + unsigned int option_flags, int size) { unsigned char tmp[9]; - struct pnp_mem *mem; + resource_size_t base, len; + unsigned char flags; isapnp_peek(tmp, size); - mem = kzalloc(sizeof(struct pnp_mem), GFP_KERNEL); - if (!mem) - return; - mem->min = mem->max = - (tmp[4] << 24) | (tmp[3] << 16) | (tmp[2] << 8) | tmp[1]; - mem->size = (tmp[8] << 24) | (tmp[7] << 16) | (tmp[6] << 8) | tmp[5]; - mem->align = 0; - mem->flags = tmp[0]; - pnp_register_mem_resource(option, mem); + base = (tmp[4] << 24) | (tmp[3] << 16) | (tmp[2] << 8) | tmp[1]; + len = (tmp[8] << 24) | (tmp[7] << 16) | (tmp[6] << 8) | tmp[5]; + flags = tmp[0]; + pnp_register_mem_resource(dev, option_flags, base, base, 0, len, flags); } /* @@ -609,18 +580,16 @@ isapnp_parse_name(char *name, unsigned int name_max, unsigned short *size) static int __init isapnp_create_device(struct pnp_card *card, unsigned short size) { - int number = 0, skip = 0, priority = 0, compat = 0; + int number = 0, skip = 0, priority, compat = 0; unsigned char type, tmp[17]; - struct pnp_option *option; + unsigned int option_flags; struct pnp_dev *dev; + u32 eisa_id; + char id[8]; if ((dev = isapnp_parse_device(card, size, number++)) == NULL) return 1; - option = pnp_register_independent_option(dev); - if (!option) { - kfree(dev); - return 1; - } + option_flags = 0; pnp_add_card_device(card, dev); while (1) { @@ -637,23 +606,20 @@ static int __init isapnp_create_device(struct pnp_card *card, return 1; size = 0; skip = 0; - option = pnp_register_independent_option(dev); - if (!option) { - kfree(dev); - return 1; - } + option_flags = 0; pnp_add_card_device(card, dev); } else { skip = 1; } - priority = 0; compat = 0; break; case _STAG_COMPATDEVID: if (size == 4 && compat < DEVICE_COUNT_COMPATIBLE) { isapnp_peek(tmp, 4); - isapnp_parse_id(dev, (tmp[1] << 8) | tmp[0], - (tmp[3] << 8) | tmp[2]); + eisa_id = tmp[0] | tmp[1] << 8 | + tmp[2] << 16 | tmp[3] << 24; + pnp_eisa_id_to_string(eisa_id, id); + pnp_add_id(dev, id); compat++; size = 0; } @@ -661,43 +627,42 @@ static int __init isapnp_create_device(struct pnp_card *card, case _STAG_IRQ: if (size < 2 || size > 3) goto __skip; - isapnp_parse_irq_resource(option, size); + isapnp_parse_irq_resource(dev, option_flags, size); size = 0; break; case _STAG_DMA: if (size != 2) goto __skip; - isapnp_parse_dma_resource(option, size); + isapnp_parse_dma_resource(dev, option_flags, size); size = 0; break; case _STAG_STARTDEP: if (size > 1) goto __skip; - priority = 0x100 | PNP_RES_PRIORITY_ACCEPTABLE; + priority = PNP_RES_PRIORITY_ACCEPTABLE; if (size > 0) { isapnp_peek(tmp, size); - priority = 0x100 | tmp[0]; + priority = tmp[0]; size = 0; } - option = pnp_register_dependent_option(dev, priority); - if (!option) - return 1; + option_flags = pnp_new_dependent_set(dev, priority); break; case _STAG_ENDDEP: if (size != 0) goto __skip; - priority = 0; + option_flags = 0; break; case _STAG_IOPORT: if (size != 7) goto __skip; - isapnp_parse_port_resource(option, size); + isapnp_parse_port_resource(dev, option_flags, size); size = 0; break; case _STAG_FIXEDIO: if (size != 3) goto __skip; - isapnp_parse_fixed_port_resource(option, size); + isapnp_parse_fixed_port_resource(dev, option_flags, + size); size = 0; break; case _STAG_VENDOR: @@ -705,7 +670,7 @@ static int __init isapnp_create_device(struct pnp_card *card, case _LTAG_MEMRANGE: if (size != 9) goto __skip; - isapnp_parse_mem_resource(option, size); + isapnp_parse_mem_resource(dev, option_flags, size); size = 0; break; case _LTAG_ANSISTR: @@ -720,13 +685,14 @@ static int __init isapnp_create_device(struct pnp_card *card, case _LTAG_MEM32RANGE: if (size != 17) goto __skip; - isapnp_parse_mem32_resource(option, size); + isapnp_parse_mem32_resource(dev, option_flags, size); size = 0; break; case _LTAG_FIXEDMEM32RANGE: if (size != 9) goto __skip; - isapnp_parse_fixed_mem32_resource(option, size); + isapnp_parse_fixed_mem32_resource(dev, option_flags, + size); size = 0; break; case _STAG_END: @@ -734,9 +700,8 @@ static int __init isapnp_create_device(struct pnp_card *card, isapnp_skip_bytes(size); return 1; default: - printk(KERN_ERR - "isapnp: unexpected or unknown tag type 0x%x for logical device %i (device %i), ignored\n", - type, dev->number, card->number); + dev_err(&dev->dev, "unknown tag %#x (card %i), " + "ignored\n", type, card->number); } __skip: if (size > 0) @@ -789,9 +754,8 @@ static void __init isapnp_parse_resource_map(struct pnp_card *card) isapnp_skip_bytes(size); return; default: - printk(KERN_ERR - "isapnp: unexpected or unknown tag type 0x%x for device %i, ignored\n", - type, card->number); + dev_err(&card->dev, "unknown tag %#x, ignored\n", + type); } __skip: if (size > 0) @@ -822,25 +786,6 @@ static unsigned char __init isapnp_checksum(unsigned char *data) } /* - * Parse EISA id for ISA PnP card. - */ -static void isapnp_parse_card_id(struct pnp_card *card, unsigned short vendor, - unsigned short device) -{ - struct pnp_id *id = kzalloc(sizeof(struct pnp_id), GFP_KERNEL); - - if (!id) - return; - sprintf(id->id, "%c%c%c%x%x%x%x", - 'A' + ((vendor >> 2) & 0x3f) - 1, - 'A' + (((vendor & 3) << 3) | ((vendor >> 13) & 7)) - 1, - 'A' + ((vendor >> 8) & 0x1f) - 1, - (device >> 4) & 0x0f, - device & 0x0f, (device >> 12) & 0x0f, (device >> 8) & 0x0f); - pnp_add_card_id(id, card); -} - -/* * Build device list for all present ISA PnP devices. */ static int __init isapnp_build_device_list(void) @@ -848,6 +793,8 @@ static int __init isapnp_build_device_list(void) int csn; unsigned char header[9], checksum; struct pnp_card *card; + u32 eisa_id; + char id[8]; isapnp_wait(); isapnp_key(); @@ -855,32 +802,23 @@ static int __init isapnp_build_device_list(void) isapnp_wake(csn); isapnp_peek(header, 9); checksum = isapnp_checksum(header); -#if 0 - printk(KERN_DEBUG - "vendor: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", - header[0], header[1], header[2], header[3], header[4], - header[5], header[6], header[7], header[8]); - printk(KERN_DEBUG "checksum = 0x%x\n", checksum); -#endif - if ((card = - kzalloc(sizeof(struct pnp_card), GFP_KERNEL)) == NULL) + eisa_id = header[0] | header[1] << 8 | + header[2] << 16 | header[3] << 24; + pnp_eisa_id_to_string(eisa_id, id); + card = pnp_alloc_card(&isapnp_protocol, csn, id); + if (!card) continue; - card->number = csn; INIT_LIST_HEAD(&card->devices); - isapnp_parse_card_id(card, (header[1] << 8) | header[0], - (header[3] << 8) | header[2]); card->serial = (header[7] << 24) | (header[6] << 16) | (header[5] << 8) | header[4]; isapnp_checksum_value = 0x00; isapnp_parse_resource_map(card); if (isapnp_checksum_value != 0x00) - printk(KERN_ERR - "isapnp: checksum for device %i is not valid (0x%x)\n", - csn, isapnp_checksum_value); + dev_err(&card->dev, "invalid checksum %#x\n", + isapnp_checksum_value); card->checksum = isapnp_checksum_value; - card->protocol = &isapnp_protocol; pnp_add_card(card); } @@ -947,100 +885,87 @@ EXPORT_SYMBOL(isapnp_cfg_begin); EXPORT_SYMBOL(isapnp_cfg_end); EXPORT_SYMBOL(isapnp_write_byte); -static int isapnp_read_resources(struct pnp_dev *dev, - struct pnp_resource_table *res) +static int isapnp_get_resources(struct pnp_dev *dev) { - int tmp, ret; + int i, ret; + pnp_dbg(&dev->dev, "get resources\n"); + pnp_init_resources(dev); + isapnp_cfg_begin(dev->card->number, dev->number); dev->active = isapnp_read_byte(ISAPNP_CFG_ACTIVATE); - if (dev->active) { - for (tmp = 0; tmp < ISAPNP_MAX_PORT; tmp++) { - ret = isapnp_read_word(ISAPNP_CFG_PORT + (tmp << 1)); - if (!ret) - continue; - res->port_resource[tmp].start = ret; - res->port_resource[tmp].flags = IORESOURCE_IO; - } - for (tmp = 0; tmp < ISAPNP_MAX_MEM; tmp++) { - ret = - isapnp_read_word(ISAPNP_CFG_MEM + (tmp << 3)) << 8; - if (!ret) - continue; - res->mem_resource[tmp].start = ret; - res->mem_resource[tmp].flags = IORESOURCE_MEM; - } - for (tmp = 0; tmp < ISAPNP_MAX_IRQ; tmp++) { - ret = - (isapnp_read_word(ISAPNP_CFG_IRQ + (tmp << 1)) >> - 8); - if (!ret) - continue; - res->irq_resource[tmp].start = - res->irq_resource[tmp].end = ret; - res->irq_resource[tmp].flags = IORESOURCE_IRQ; - } - for (tmp = 0; tmp < ISAPNP_MAX_DMA; tmp++) { - ret = isapnp_read_byte(ISAPNP_CFG_DMA + tmp); - if (ret == 4) - continue; - res->dma_resource[tmp].start = - res->dma_resource[tmp].end = ret; - res->dma_resource[tmp].flags = IORESOURCE_DMA; - } - } - return 0; -} + if (!dev->active) + goto __end; -static int isapnp_get_resources(struct pnp_dev *dev, - struct pnp_resource_table *res) -{ - int ret; + for (i = 0; i < ISAPNP_MAX_PORT; i++) { + ret = isapnp_read_word(ISAPNP_CFG_PORT + (i << 1)); + pnp_add_io_resource(dev, ret, ret, + ret == 0 ? IORESOURCE_DISABLED : 0); + } + for (i = 0; i < ISAPNP_MAX_MEM; i++) { + ret = isapnp_read_word(ISAPNP_CFG_MEM + (i << 3)) << 8; + pnp_add_mem_resource(dev, ret, ret, + ret == 0 ? IORESOURCE_DISABLED : 0); + } + for (i = 0; i < ISAPNP_MAX_IRQ; i++) { + ret = isapnp_read_word(ISAPNP_CFG_IRQ + (i << 1)) >> 8; + pnp_add_irq_resource(dev, ret, + ret == 0 ? IORESOURCE_DISABLED : 0); + } + for (i = 0; i < ISAPNP_MAX_DMA; i++) { + ret = isapnp_read_byte(ISAPNP_CFG_DMA + i); + pnp_add_dma_resource(dev, ret, + ret == 4 ? IORESOURCE_DISABLED : 0); + } - pnp_init_resource_table(res); - isapnp_cfg_begin(dev->card->number, dev->number); - ret = isapnp_read_resources(dev, res); +__end: isapnp_cfg_end(); - return ret; + return 0; } -static int isapnp_set_resources(struct pnp_dev *dev, - struct pnp_resource_table *res) +static int isapnp_set_resources(struct pnp_dev *dev) { + struct resource *res; int tmp; + pnp_dbg(&dev->dev, "set resources\n"); isapnp_cfg_begin(dev->card->number, dev->number); dev->active = 1; - for (tmp = 0; - tmp < ISAPNP_MAX_PORT - && (res->port_resource[tmp]. - flags & (IORESOURCE_IO | IORESOURCE_UNSET)) == IORESOURCE_IO; - tmp++) - isapnp_write_word(ISAPNP_CFG_PORT + (tmp << 1), - res->port_resource[tmp].start); - for (tmp = 0; - tmp < ISAPNP_MAX_IRQ - && (res->irq_resource[tmp]. - flags & (IORESOURCE_IRQ | IORESOURCE_UNSET)) == IORESOURCE_IRQ; - tmp++) { - int irq = res->irq_resource[tmp].start; - if (irq == 2) - irq = 9; - isapnp_write_byte(ISAPNP_CFG_IRQ + (tmp << 1), irq); + for (tmp = 0; tmp < ISAPNP_MAX_PORT; tmp++) { + res = pnp_get_resource(dev, IORESOURCE_IO, tmp); + if (pnp_resource_enabled(res)) { + pnp_dbg(&dev->dev, " set io %d to %#llx\n", + tmp, (unsigned long long) res->start); + isapnp_write_word(ISAPNP_CFG_PORT + (tmp << 1), + res->start); + } + } + for (tmp = 0; tmp < ISAPNP_MAX_IRQ; tmp++) { + res = pnp_get_resource(dev, IORESOURCE_IRQ, tmp); + if (pnp_resource_enabled(res)) { + int irq = res->start; + if (irq == 2) + irq = 9; + pnp_dbg(&dev->dev, " set irq %d to %d\n", tmp, irq); + isapnp_write_byte(ISAPNP_CFG_IRQ + (tmp << 1), irq); + } + } + for (tmp = 0; tmp < ISAPNP_MAX_DMA; tmp++) { + res = pnp_get_resource(dev, IORESOURCE_DMA, tmp); + if (pnp_resource_enabled(res)) { + pnp_dbg(&dev->dev, " set dma %d to %lld\n", + tmp, (unsigned long long) res->start); + isapnp_write_byte(ISAPNP_CFG_DMA + tmp, res->start); + } + } + for (tmp = 0; tmp < ISAPNP_MAX_MEM; tmp++) { + res = pnp_get_resource(dev, IORESOURCE_MEM, tmp); + if (pnp_resource_enabled(res)) { + pnp_dbg(&dev->dev, " set mem %d to %#llx\n", + tmp, (unsigned long long) res->start); + isapnp_write_word(ISAPNP_CFG_MEM + (tmp << 3), + (res->start >> 8) & 0xffff); + } } - for (tmp = 0; - tmp < ISAPNP_MAX_DMA - && (res->dma_resource[tmp]. - flags & (IORESOURCE_DMA | IORESOURCE_UNSET)) == IORESOURCE_DMA; - tmp++) - isapnp_write_byte(ISAPNP_CFG_DMA + tmp, - res->dma_resource[tmp].start); - for (tmp = 0; - tmp < ISAPNP_MAX_MEM - && (res->mem_resource[tmp]. - flags & (IORESOURCE_MEM | IORESOURCE_UNSET)) == IORESOURCE_MEM; - tmp++) - isapnp_write_word(ISAPNP_CFG_MEM + (tmp << 3), - (res->mem_resource[tmp].start >> 8) & 0xffff); /* FIXME: We aren't handling 32bit mems properly here */ isapnp_activate(dev->number); isapnp_cfg_end(); @@ -1075,7 +1000,7 @@ static int __init isapnp_init(void) printk(KERN_INFO "isapnp: ISA Plug & Play support disabled\n"); return 0; } -#ifdef CONFIG_PPC_MERGE +#ifdef CONFIG_PPC if (check_legacy_ioport(_PIDXR) || check_legacy_ioport(_PNPWRP)) return -EINVAL; #endif @@ -1138,13 +1063,13 @@ static int __init isapnp_init(void) protocol_for_each_card(&isapnp_protocol, card) { cards++; if (isapnp_verbose) { - printk(KERN_INFO "isapnp: Card '%s'\n", - card->name[0] ? card->name : "Unknown"); + dev_info(&card->dev, "card '%s'\n", + card->name[0] ? card->name : "unknown"); if (isapnp_verbose < 2) continue; card_for_each_dev(card, dev) { - printk(KERN_INFO "isapnp: Device '%s'\n", - dev->name[0] ? dev->name : "Unknown"); + dev_info(&card->dev, "device '%s'\n", + dev->name[0] ? dev->name : "unknown"); } } } diff --git a/drivers/pnp/isapnp/proc.c b/drivers/pnp/isapnp/proc.c index 2b8266c3d40..5edee645d89 100644 --- a/drivers/pnp/isapnp/proc.c +++ b/drivers/pnp/isapnp/proc.c @@ -21,7 +21,6 @@ #include <linux/isapnp.h> #include <linux/proc_fs.h> #include <linux/init.h> -#include <linux/smp_lock.h> #include <asm/uaccess.h> extern struct pnp_protocol isapnp_protocol; @@ -30,34 +29,13 @@ static struct proc_dir_entry *isapnp_proc_bus_dir = NULL; static loff_t isapnp_proc_bus_lseek(struct file *file, loff_t off, int whence) { - loff_t new = -1; - - lock_kernel(); - switch (whence) { - case 0: - new = off; - break; - case 1: - new = file->f_pos + off; - break; - case 2: - new = 256 + off; - break; - } - if (new < 0 || new > 256) { - unlock_kernel(); - return -EINVAL; - } - unlock_kernel(); - return (file->f_pos = new); + return fixed_size_llseek(file, off, whence, 256); } static ssize_t isapnp_proc_bus_read(struct file *file, char __user * buf, size_t nbytes, loff_t * ppos) { - struct inode *ino = file->f_path.dentry->d_inode; - struct proc_dir_entry *dp = PDE(ino); - struct pnp_dev *dev = dp->data; + struct pnp_dev *dev = PDE_DATA(file_inode(file)); int pos = *ppos; int cnt, size = 256; @@ -85,6 +63,7 @@ static ssize_t isapnp_proc_bus_read(struct file *file, char __user * buf, } static const struct file_operations isapnp_proc_bus_file_operations = { + .owner = THIS_MODULE, .llseek = isapnp_proc_bus_lseek, .read = isapnp_proc_bus_read, }; @@ -102,13 +81,11 @@ static int isapnp_proc_attach_device(struct pnp_dev *dev) return -ENOMEM; } sprintf(name, "%02x", dev->number); - e = dev->procent = create_proc_entry(name, S_IFREG | S_IRUGO, de); + e = dev->procent = proc_create_data(name, S_IFREG | S_IRUGO, de, + &isapnp_proc_bus_file_operations, dev); if (!e) return -ENOMEM; - e->proc_fops = &isapnp_proc_bus_file_operations; - e->owner = THIS_MODULE; - e->data = dev; - e->size = 256; + proc_set_size(e, 256); return 0; } @@ -116,7 +93,7 @@ int __init isapnp_proc_init(void) { struct pnp_dev *dev; - isapnp_proc_bus_dir = proc_mkdir("isapnp", proc_bus); + isapnp_proc_bus_dir = proc_mkdir("bus/isapnp", NULL); protocol_for_each_dev(&isapnp_protocol, dev) { isapnp_proc_attach_device(dev); } diff --git a/drivers/pnp/manager.c b/drivers/pnp/manager.c index c28caf272c1..9357aa77904 100644 --- a/drivers/pnp/manager.c +++ b/drivers/pnp/manager.c @@ -3,6 +3,8 @@ * * 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> @@ -10,109 +12,127 @@ #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" DEFINE_MUTEX(pnp_res_mutex); -static int pnp_assign_port(struct pnp_dev *dev, struct pnp_port *rule, int idx) +static struct resource *pnp_find_resource(struct pnp_dev *dev, + unsigned char rule, + unsigned long type, + unsigned int bar) { - resource_size_t *start, *end; - unsigned long *flags; + struct resource *res = pnp_get_resource(dev, type, bar); - if (idx >= PNP_MAX_PORT) { - dev_err(&dev->dev, "too many I/O port resources\n"); - /* pretend we were successful so at least the manager won't try again */ - return 1; + /* when the resource already exists, set its resource bits from rule */ + if (res) { + res->flags &= ~IORESOURCE_BITS; + res->flags |= rule & IORESOURCE_BITS; } - /* check if this resource has been manually set, if so skip */ - if (!(dev->res.port_resource[idx].flags & IORESOURCE_AUTO)) - return 1; + return res; +} + +static int pnp_assign_port(struct pnp_dev *dev, struct pnp_port *rule, int idx) +{ + struct resource *res, local_res; - start = &dev->res.port_resource[idx].start; - end = &dev->res.port_resource[idx].end; - flags = &dev->res.port_resource[idx].flags; + res = pnp_find_resource(dev, rule->flags, IORESOURCE_IO, idx); + if (res) { + 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 0; + } - /* set the initial values */ - *flags |= rule->flags | IORESOURCE_IO; - *flags &= ~IORESOURCE_UNSET; + res = &local_res; + res->flags = rule->flags | IORESOURCE_AUTO; + res->start = 0; + res->end = 0; if (!rule->size) { - *flags |= IORESOURCE_DISABLED; - return 1; /* skip disabled resource requests */ + res->flags |= IORESOURCE_DISABLED; + pnp_dbg(&dev->dev, " io %d disabled\n", idx); + goto __add; } - *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; + res->start = rule->min; + res->end = res->start + rule->size - 1; + + while (!pnp_check_port(dev, res)) { + res->start += rule->align; + res->end = res->start + rule->size - 1; + if (res->start > rule->max || !rule->align) { + 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 -EBUSY; + } } - return 1; + +__add: + pnp_add_io_resource(dev, res->start, res->end, res->flags); + return 0; } static int pnp_assign_mem(struct pnp_dev *dev, struct pnp_mem *rule, int idx) { - resource_size_t *start, *end; - unsigned long *flags; + struct resource *res, local_res; - if (idx >= PNP_MAX_MEM) { - dev_err(&dev->dev, "too many memory resources\n"); - /* pretend we were successful so at least the manager won't try again */ - return 1; + res = pnp_find_resource(dev, rule->flags, IORESOURCE_MEM, idx); + if (res) { + 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 0; } - /* 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; + res = &local_res; + res->flags = rule->flags | IORESOURCE_AUTO; + res->start = 0; + res->end = 0; - /* convert pnp flags to standard Linux flags */ + /* ??? rule->flags restricted to 8 bits, all tests bogus ??? */ if (!(rule->flags & IORESOURCE_MEM_WRITEABLE)) - *flags |= IORESOURCE_READONLY; + res->flags |= IORESOURCE_READONLY; if (rule->flags & IORESOURCE_MEM_CACHEABLE) - *flags |= IORESOURCE_CACHEABLE; + res->flags |= IORESOURCE_CACHEABLE; if (rule->flags & IORESOURCE_MEM_RANGELENGTH) - *flags |= IORESOURCE_RANGELENGTH; + res->flags |= IORESOURCE_RANGELENGTH; if (rule->flags & IORESOURCE_MEM_SHADOWABLE) - *flags |= IORESOURCE_SHADOWABLE; + res->flags |= IORESOURCE_SHADOWABLE; if (!rule->size) { - *flags |= IORESOURCE_DISABLED; - return 1; /* skip disabled resource requests */ + res->flags |= IORESOURCE_DISABLED; + pnp_dbg(&dev->dev, " mem %d disabled\n", idx); + goto __add; } - *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; + res->start = rule->min; + res->end = res->start + rule->size - 1; + + while (!pnp_check_mem(dev, res)) { + res->start += rule->align; + res->end = res->start + rule->size - 1; + if (res->start > rule->max || !rule->align) { + 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 -EBUSY; + } } - return 1; + +__add: + pnp_add_mem_resource(dev, res->start, res->end, res->flags); + return 0; } static int pnp_assign_irq(struct pnp_dev *dev, struct pnp_irq *rule, int idx) { - resource_size_t *start, *end; - unsigned long *flags; + struct resource *res, local_res; int i; /* IRQ priority: this table is good for i386 */ @@ -120,49 +140,58 @@ static int pnp_assign_irq(struct pnp_dev *dev, struct pnp_irq *rule, int idx) 5, 10, 11, 12, 9, 14, 15, 7, 3, 4, 13, 0, 1, 6, 8, 2 }; - if (idx >= PNP_MAX_IRQ) { - dev_err(&dev->dev, "too many IRQ resources\n"); - /* pretend we were successful so at least the manager won't try again */ - return 1; + res = pnp_find_resource(dev, rule->flags, IORESOURCE_IRQ, idx); + if (res) { + pnp_dbg(&dev->dev, " irq %d already set to %d flags %#lx\n", + idx, (int) res->start, res->flags); + return 0; } - /* 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; + res = &local_res; + res->flags = rule->flags | IORESOURCE_AUTO; + res->start = -1; + res->end = -1; - if (bitmap_empty(rule->map, PNP_IRQ_NR)) { - *flags |= IORESOURCE_DISABLED; - return 1; /* skip disabled resource requests */ + if (bitmap_empty(rule->map.bits, PNP_IRQ_NR)) { + res->flags |= IORESOURCE_DISABLED; + pnp_dbg(&dev->dev, " irq %d disabled\n", idx); + goto __add; } /* 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; + res->start = find_next_bit(rule->map.bits, PNP_IRQ_NR, 16); + if (res->start < PNP_IRQ_NR) { + res->end = res->start; + goto __add; } 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; + if (test_bit(xtab[i], rule->map.bits)) { + res->start = res->end = xtab[i]; + if (pnp_check_irq(dev, res)) + goto __add; } } + + 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 0; } -static void pnp_assign_dma(struct pnp_dev *dev, struct pnp_dma *rule, int idx) +#ifdef CONFIG_ISA_DMA_API +static int pnp_assign_dma(struct pnp_dev *dev, struct pnp_dma *rule, int idx) { - resource_size_t *start, *end; - unsigned long *flags; + struct resource *res, local_res; int i; /* DMA priority: this table is good for i386 */ @@ -170,263 +199,107 @@ static void pnp_assign_dma(struct pnp_dev *dev, struct pnp_dma *rule, int idx) 1, 3, 5, 6, 7, 0, 2, 4 }; - if (idx >= PNP_MAX_DMA) { - dev_err(&dev->dev, "too many DMA resources\n"); - return; + res = pnp_find_resource(dev, rule->flags, IORESOURCE_DMA, idx); + if (res) { + pnp_dbg(&dev->dev, " dma %d already set to %d flags %#lx\n", + idx, (int) res->start, res->flags); + return 0; } - /* check if this resource has been manually set, if so skip */ - if (!(dev->res.dma_resource[idx].flags & IORESOURCE_AUTO)) - return; + res = &local_res; + res->flags = rule->flags | IORESOURCE_AUTO; + res->start = -1; + res->end = -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) { + res->flags |= IORESOURCE_DISABLED; + pnp_dbg(&dev->dev, " dma %d disabled\n", idx); + goto __add; + } for (i = 0; i < 8; i++) { if (rule->map & (1 << xtab[i])) { - *start = *end = xtab[i]; - if (pnp_check_dma(dev, idx)) - return; + res->start = res->end = xtab[i]; + if (pnp_check_dma(dev, res)) + goto __add; } } -#ifdef MAX_DMA_CHANNELS - *start = *end = MAX_DMA_CHANNELS; -#endif - *flags |= IORESOURCE_UNSET | IORESOURCE_DISABLED; -} -/** - * 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_dbg(&dev->dev, " couldn't assign dma %d\n", idx); + return -EBUSY; + +__add: + pnp_add_dma_resource(dev, res->start, res->flags); + return 0; } +#endif /* CONFIG_ISA_DMA_API */ -/** - * 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) +void pnp_init_resources(struct pnp_dev *dev) { - 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_free_resources(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. - */ -static int pnp_assign_resources(struct pnp_dev *dev, int depnum) +static void pnp_clean_resource_table(struct pnp_dev *dev) { - 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; + struct pnp_resource *pnp_res, *tmp; - mutex_lock(&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) { - pnp_assign_dma(dev, dma, ndma); - ndma++; - dma = dma->next; - } + list_for_each_entry_safe(pnp_res, tmp, &dev->resources, list) { + if (pnp_res->res.flags & IORESOURCE_AUTO) + pnp_free_resource(pnp_res); } - - 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) { - pnp_assign_dma(dev, dma, ndma); - ndma++; - dma = dma->next; - } - } else if (dev->dependent) - goto fail; - - mutex_unlock(&pnp_res_mutex); - return 1; - -fail: - pnp_clean_resource_table(&dev->res); - mutex_unlock(&pnp_res_mutex); - return 0; } /** - * pnp_manual_config_dev - Disables Auto Config and Manually sets the resource table + * pnp_assign_resources - assigns resources to the device based on the specified dependent number * @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. + * @set: the dependent function number */ -int pnp_manual_config_dev(struct pnp_dev *dev, struct pnp_resource_table *res, - int mode) +static int pnp_assign_resources(struct pnp_dev *dev, int set) { - int i; - struct pnp_resource_table *bak; - - if (!pnp_can_configure(dev)) - return -ENODEV; - bak = pnp_alloc(sizeof(struct pnp_resource_table)); - if (!bak) - return -ENOMEM; - *bak = dev->res; + struct pnp_option *option; + int nport = 0, nmem = 0, nirq = 0; + int ndma __maybe_unused = 0; + int ret = 0; + pnp_dbg(&dev->dev, "pnp_assign_resources, try dependent set %d\n", set); mutex_lock(&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; + pnp_clean_resource_table(dev); + + 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; +#ifdef CONFIG_ISA_DMA_API + case IORESOURCE_DMA: + ret = pnp_assign_dma(dev, &option->u.dma, ndma++); + break; +#endif + default: + ret = -EINVAL; + break; } + if (ret < 0) + break; } - mutex_unlock(&pnp_res_mutex); - - kfree(bak); - return 0; -fail: - dev->res = *bak; mutex_unlock(&pnp_res_mutex); - kfree(bak); - return -EINVAL; + 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; } /** @@ -435,29 +308,25 @@ fail: */ 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; } /** @@ -469,11 +338,12 @@ int pnp_auto_config_dev(struct pnp_dev *dev) 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; } - if (dev->protocol->set(dev, &dev->res) < 0) { + dbg_pnp_show_resources(dev, "pnp_start_dev"); + if (dev->protocol->set(dev) < 0) { dev_err(&dev->dev, "activation failed\n"); return -EIO; } @@ -491,7 +361,7 @@ int pnp_start_dev(struct pnp_dev *dev) 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) { @@ -549,30 +419,13 @@ int pnp_disable_dev(struct pnp_dev *dev) /* release the resources so that other devices can use them */ mutex_lock(&pnp_res_mutex); - pnp_clean_resource_table(&dev->res); + pnp_clean_resource_table(dev); mutex_unlock(&pnp_res_mutex); return 0; } -/** - * 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, resource_size_t start, - resource_size_t size) -{ - resource->flags &= ~(IORESOURCE_AUTO | IORESOURCE_UNSET); - resource->start = start; - resource->end = start + size - 1; -} - -EXPORT_SYMBOL(pnp_manual_config_dev); EXPORT_SYMBOL(pnp_start_dev); EXPORT_SYMBOL(pnp_stop_dev); EXPORT_SYMBOL(pnp_activate_dev); EXPORT_SYMBOL(pnp_disable_dev); -EXPORT_SYMBOL(pnp_resource_change); -EXPORT_SYMBOL(pnp_init_resource_table); diff --git a/drivers/pnp/pnpacpi/Makefile b/drivers/pnp/pnpacpi/Makefile index 905326fcca8..40c93da1825 100644 --- a/drivers/pnp/pnpacpi/Makefile +++ b/drivers/pnp/pnpacpi/Makefile @@ -1,5 +1,6 @@ # # Makefile for the kernel PNPACPI driver. # +obj-y += pnp.o -obj-y := core.o rsparser.o +pnp-y := core.o rsparser.o diff --git a/drivers/pnp/pnpacpi/core.c b/drivers/pnp/pnpacpi/core.c index c283a9a70d8..a5c6cb773e5 100644 --- a/drivers/pnp/pnpacpi/core.c +++ b/drivers/pnp/pnpacpi/core.c @@ -19,35 +19,16 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include <linux/export.h> #include <linux/acpi.h> #include <linux/pnp.h> +#include <linux/slab.h> #include <linux/mod_devicetable.h> -#include <acpi/acpi_bus.h> -#include <acpi/actypes.h> +#include "../base.h" #include "pnpacpi.h" -static int num = 0; - -/* We need only to blacklist devices that have already an acpi driver that - * can't use pnp layer. We don't need to blacklist device that are directly - * used by the kernel (PCI root, ...), as it is harmless and there were - * already present in pnpbios. But there is an exception for devices that - * have irqs (PIC, Timer) because we call acpi_register_gsi. - * Finally, only devices that have a CRS method need to be in this list. - */ -static struct acpi_device_id excluded_id_list[] __initdata = { - {"PNP0C09", 0}, /* EC */ - {"PNP0C0F", 0}, /* Link device */ - {"PNP0000", 0}, /* PIC */ - {"PNP0100", 0}, /* Timer */ - {"", 0}, -}; - -static inline int is_exclusive_device(struct acpi_device *dev) -{ - return (!acpi_match_device_ids(dev, excluded_id_list)); -} +static int num; /* * Compatible Device IDs @@ -56,9 +37,9 @@ static inline int is_exclusive_device(struct acpi_device *dev) if (!(('0' <= (c) && (c) <= '9') || ('A' <= (c) && (c) <= 'F'))) \ return 0 #define TEST_ALPHA(c) \ - if (!('@' <= (c) || (c) <= 'Z')) \ + if (!('A' <= (c) && (c) <= 'Z')) \ return 0 -static int __init ispnpidacpi(char *id) +static int __init ispnpidacpi(const char *id) { TEST_ALPHA(id[0]); TEST_ALPHA(id[1]); @@ -72,191 +53,251 @@ static int __init ispnpidacpi(char *id) return 1; } -static void __init pnpidacpi_to_pnpid(char *id, char *str) +static int pnpacpi_get_resources(struct pnp_dev *dev) { - str[0] = id[0]; - str[1] = id[1]; - str[2] = id[2]; - str[3] = tolower(id[3]); - str[4] = tolower(id[4]); - str[5] = tolower(id[5]); - str[6] = tolower(id[6]); - str[7] = '\0'; + pnp_dbg(&dev->dev, "get resources\n"); + return pnpacpi_parse_allocated_resource(dev); } -static int pnpacpi_get_resources(struct pnp_dev *dev, - struct pnp_resource_table *res) +static int pnpacpi_set_resources(struct pnp_dev *dev) { - acpi_status status; + struct acpi_device *acpi_dev; + acpi_handle handle; + int ret = 0; - status = pnpacpi_parse_allocated_resource((acpi_handle) dev->data, - &dev->res); - return ACPI_FAILURE(status) ? -ENODEV : 0; -} + pnp_dbg(&dev->dev, "set resources\n"); -static int pnpacpi_set_resources(struct pnp_dev *dev, - struct pnp_resource_table *res) -{ - acpi_handle handle = dev->data; - struct acpi_buffer buffer; - int ret = 0; - acpi_status status; + handle = ACPI_HANDLE(&dev->dev); + if (!handle || acpi_bus_get_device(handle, &acpi_dev)) { + dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__); + return -ENODEV; + } + + if (WARN_ON_ONCE(acpi_dev != dev->data)) + dev->data = acpi_dev; + + if (acpi_has_method(handle, METHOD_NAME__SRS)) { + struct acpi_buffer buffer; - ret = pnpacpi_build_resource_template(handle, &buffer); - if (ret) - return ret; - ret = pnpacpi_encode_resources(res, &buffer); - if (ret) { + ret = pnpacpi_build_resource_template(dev, &buffer); + if (ret) + return ret; + + ret = pnpacpi_encode_resources(dev, &buffer); + if (!ret) { + acpi_status status; + + status = acpi_set_current_resources(handle, &buffer); + if (ACPI_FAILURE(status)) + ret = -EIO; + } kfree(buffer.pointer); - return ret; } - status = acpi_set_current_resources(handle, &buffer); - if (ACPI_FAILURE(status)) - ret = -EINVAL; - kfree(buffer.pointer); + if (!ret && acpi_bus_power_manageable(handle)) + ret = acpi_bus_set_power(handle, ACPI_STATE_D0); + return ret; } static int pnpacpi_disable_resources(struct pnp_dev *dev) { + struct acpi_device *acpi_dev; + acpi_handle handle; acpi_status status; + dev_dbg(&dev->dev, "disable resources\n"); + + handle = ACPI_HANDLE(&dev->dev); + if (!handle || acpi_bus_get_device(handle, &acpi_dev)) { + dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__); + return 0; + } + /* acpi_unregister_gsi(pnp_irq(dev, 0)); */ - status = acpi_evaluate_object((acpi_handle) dev->data, - "_DIS", NULL, NULL); - return ACPI_FAILURE(status) ? -ENODEV : 0; + if (acpi_bus_power_manageable(handle)) + acpi_bus_set_power(handle, ACPI_STATE_D3_COLD); + + /* continue even if acpi_bus_set_power() fails */ + status = acpi_evaluate_object(handle, "_DIS", NULL, NULL); + if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) + return -ENODEV; + + return 0; } #ifdef CONFIG_ACPI_SLEEP +static bool pnpacpi_can_wakeup(struct pnp_dev *dev) +{ + struct acpi_device *acpi_dev; + acpi_handle handle; + + handle = ACPI_HANDLE(&dev->dev); + if (!handle || acpi_bus_get_device(handle, &acpi_dev)) { + dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__); + return false; + } + + return acpi_bus_can_wakeup(handle); +} + static int pnpacpi_suspend(struct pnp_dev *dev, pm_message_t state) { - int power_state; + struct acpi_device *acpi_dev; + acpi_handle handle; + int error = 0; + + handle = ACPI_HANDLE(&dev->dev); + if (!handle || acpi_bus_get_device(handle, &acpi_dev)) { + dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__); + return 0; + } - power_state = acpi_pm_device_sleep_state(&dev->dev, - device_may_wakeup(&dev->dev), - NULL); - if (power_state < 0) - power_state = (state.event == PM_EVENT_ON) ? - ACPI_STATE_D0 : ACPI_STATE_D3; + if (device_can_wakeup(&dev->dev)) { + error = acpi_pm_device_sleep_wake(&dev->dev, + device_may_wakeup(&dev->dev)); + if (error) + return error; + } + + if (acpi_bus_power_manageable(handle)) { + int power_state = acpi_pm_device_sleep_state(&dev->dev, NULL, + ACPI_STATE_D3_COLD); + if (power_state < 0) + power_state = (state.event == PM_EVENT_ON) ? + ACPI_STATE_D0 : ACPI_STATE_D3_COLD; + + /* + * acpi_bus_set_power() often fails (keyboard port can't be + * powered-down?), and in any case, our return value is ignored + * by pnp_bus_suspend(). Hence we don't revert the wakeup + * setting if the set_power fails. + */ + error = acpi_bus_set_power(handle, power_state); + } - return acpi_bus_set_power((acpi_handle) dev->data, power_state); + return error; } static int pnpacpi_resume(struct pnp_dev *dev) { - return acpi_bus_set_power((acpi_handle) dev->data, ACPI_STATE_D0); + struct acpi_device *acpi_dev; + acpi_handle handle = ACPI_HANDLE(&dev->dev); + int error = 0; + + if (!handle || acpi_bus_get_device(handle, &acpi_dev)) { + dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__); + return -ENODEV; + } + + if (device_may_wakeup(&dev->dev)) + acpi_pm_device_sleep_wake(&dev->dev, false); + + if (acpi_bus_power_manageable(handle)) + error = acpi_bus_set_power(handle, ACPI_STATE_D0); + + return error; } #endif -static struct pnp_protocol pnpacpi_protocol = { +struct pnp_protocol pnpacpi_protocol = { .name = "Plug and Play ACPI", .get = pnpacpi_get_resources, .set = pnpacpi_set_resources, .disable = pnpacpi_disable_resources, #ifdef CONFIG_ACPI_SLEEP + .can_wakeup = pnpacpi_can_wakeup, .suspend = pnpacpi_suspend, .resume = pnpacpi_resume, #endif }; +EXPORT_SYMBOL(pnpacpi_protocol); + +static char *__init pnpacpi_get_id(struct acpi_device *device) +{ + struct acpi_hardware_id *id; + + list_for_each_entry(id, &device->pnp.ids, list) { + if (ispnpidacpi(id->id)) + return id->id; + } + + return NULL; +} static int __init pnpacpi_add_device(struct acpi_device *device) { - acpi_handle temp = NULL; - acpi_status status; - struct pnp_id *dev_id; struct pnp_dev *dev; + char *pnpid; + struct acpi_hardware_id *id; + int error; - status = acpi_get_handle(device->handle, "_CRS", &temp); - if (ACPI_FAILURE(status) || !ispnpidacpi(acpi_device_hid(device)) || - is_exclusive_device(device)) + /* Skip devices that are already bound */ + if (device->physical_node_count) return 0; - dev = kzalloc(sizeof(struct pnp_dev), GFP_KERNEL); - if (!dev) { - pnp_err("Out of memory"); + /* + * If a PnPacpi device is not present , the device + * driver should not be loaded. + */ + if (!acpi_has_method(device->handle, "_CRS")) + return 0; + + pnpid = pnpacpi_get_id(device); + if (!pnpid) + return 0; + + if (!device->status.present) + return 0; + + dev = pnp_alloc_dev(&pnpacpi_protocol, num, pnpid); + if (!dev) return -ENOMEM; - } - dev->data = device->handle; + + dev->data = device; /* .enabled means the device can decode the resources */ dev->active = device->status.enabled; - status = acpi_get_handle(device->handle, "_SRS", &temp); - if (ACPI_SUCCESS(status)) + if (acpi_has_method(device->handle, "_SRS")) dev->capabilities |= PNP_CONFIGURABLE; dev->capabilities |= PNP_READ; if (device->flags.dynamic_status && (dev->capabilities & PNP_CONFIGURABLE)) dev->capabilities |= PNP_WRITE; if (device->flags.removable) dev->capabilities |= PNP_REMOVABLE; - status = acpi_get_handle(device->handle, "_DIS", &temp); - if (ACPI_SUCCESS(status)) + if (acpi_has_method(device->handle, "_DIS")) dev->capabilities |= PNP_DISABLE; - dev->protocol = &pnpacpi_protocol; - if (strlen(acpi_device_name(device))) strncpy(dev->name, acpi_device_name(device), sizeof(dev->name)); else strncpy(dev->name, acpi_device_bid(device), sizeof(dev->name)); - dev->number = num; - - /* set the initial values for the PnP device */ - dev_id = kzalloc(sizeof(struct pnp_id), GFP_KERNEL); - if (!dev_id) - goto err; - pnpidacpi_to_pnpid(acpi_device_hid(device), dev_id->id); - pnp_add_id(dev_id, dev); - - if (dev->active) { - /* parse allocated resource */ - status = pnpacpi_parse_allocated_resource(device->handle, - &dev->res); - if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) { - pnp_err("PnPACPI: METHOD_NAME__CRS failure for %s", - dev_id->id); - goto err1; - } - } - - if (dev->capabilities & PNP_CONFIGURABLE) { - status = pnpacpi_parse_resource_option_data(device->handle, - dev); - if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) { - pnp_err("PnPACPI: METHOD_NAME__PRS failure for %s", - dev_id->id); - goto err1; - } - } - - /* parse compatible ids */ - if (device->flags.compatible_ids) { - struct acpi_compatible_id_list *cid_list = device->pnp.cid_list; - int i; + if (dev->active) + pnpacpi_parse_allocated_resource(dev); - for (i = 0; i < cid_list->count; i++) { - if (!ispnpidacpi(cid_list->id[i].value)) - continue; - dev_id = kzalloc(sizeof(struct pnp_id), GFP_KERNEL); - if (!dev_id) - continue; + if (dev->capabilities & PNP_CONFIGURABLE) + pnpacpi_parse_resource_option_data(dev); - pnpidacpi_to_pnpid(cid_list->id[i].value, dev_id->id); - pnp_add_id(dev_id, dev); - } + list_for_each_entry(id, &device->pnp.ids, list) { + if (!strcmp(id->id, pnpid)) + continue; + if (!ispnpidacpi(id->id)) + continue; + pnp_add_id(dev, id->id); } /* clear out the damaged flags */ if (!dev->active) - pnp_init_resource_table(&dev->res); - pnp_add_device(dev); + pnp_init_resources(dev); + + error = pnp_add_device(dev); + if (error) { + put_device(&dev->dev); + return error; + } + num++; - return AE_OK; -err1: - kfree(dev_id); -err: - kfree(dev); - return -EINVAL; + return 0; } static acpi_status __init pnpacpi_add_device_handler(acpi_handle handle, @@ -265,10 +306,10 @@ static acpi_status __init pnpacpi_add_device_handler(acpi_handle handle, { struct acpi_device *device; - if (!acpi_bus_get_device(handle, &device)) - pnpacpi_add_device(device); - else + if (acpi_bus_get_device(handle, &device)) return AE_CTRL_DEPTH; + if (acpi_is_pnp_device(device)) + pnpacpi_add_device(device); return AE_OK; } @@ -278,53 +319,52 @@ static int __init acpi_pnp_match(struct device *dev, void *_pnp) struct pnp_dev *pnp = _pnp; /* true means it matched */ - return acpi->flags.hardware_id - && !acpi_get_physical_device(acpi->handle) - && compare_pnp_id(pnp->id, acpi->pnp.hardware_id); + return pnp->data == acpi; } -static int __init acpi_pnp_find_device(struct device *dev, acpi_handle * handle) +static struct acpi_device * __init acpi_pnp_find_companion(struct device *dev) { - struct device *adev; - struct acpi_device *acpi; - - adev = bus_find_device(&acpi_bus_type, NULL, - to_pnp_dev(dev), acpi_pnp_match); - if (!adev) - return -ENODEV; + dev = bus_find_device(&acpi_bus_type, NULL, to_pnp_dev(dev), + acpi_pnp_match); + if (!dev) + return NULL; - acpi = to_acpi_device(adev); - *handle = acpi->handle; - put_device(adev); - return 0; + put_device(dev); + return to_acpi_device(dev); } /* complete initialization of a PNPACPI device includes having * pnpdev->dev.archdata.acpi_handle point to its ACPI sibling. */ +static bool acpi_pnp_bus_match(struct device *dev) +{ + return dev->bus == &pnp_bus_type; +} + static struct acpi_bus_type __initdata acpi_pnp_bus = { - .bus = &pnp_bus_type, - .find_device = acpi_pnp_find_device, + .name = "PNP", + .match = acpi_pnp_bus_match, + .find_companion = acpi_pnp_find_companion, }; int pnpacpi_disabled __initdata; static int __init pnpacpi_init(void) { if (acpi_disabled || pnpacpi_disabled) { - pnp_info("PnP ACPI: disabled"); + printk(KERN_INFO "pnp: PnP ACPI: disabled\n"); return 0; } - pnp_info("PnP ACPI init"); + printk(KERN_INFO "pnp: PnP ACPI init\n"); pnp_register_protocol(&pnpacpi_protocol); register_acpi_bus_type(&acpi_pnp_bus); acpi_get_devices(NULL, pnpacpi_add_device_handler, NULL, NULL); - pnp_info("PnP ACPI: found %d devices", num); + printk(KERN_INFO "pnp: PnP ACPI: found %d devices\n", num); unregister_acpi_bus_type(&acpi_pnp_bus); pnp_platform_devices = 1; return 0; } -subsys_initcall(pnpacpi_init); +fs_initcall(pnpacpi_init); static int __init pnpacpi_setup(char *str) { diff --git a/drivers/pnp/pnpacpi/pnpacpi.h b/drivers/pnp/pnpacpi/pnpacpi.h index f28e2ed66fa..051ef969977 100644 --- a/drivers/pnp/pnpacpi/pnpacpi.h +++ b/drivers/pnp/pnpacpi/pnpacpi.h @@ -1,12 +1,11 @@ #ifndef ACPI_PNP_H #define ACPI_PNP_H -#include <acpi/acpi_bus.h> #include <linux/acpi.h> #include <linux/pnp.h> -acpi_status pnpacpi_parse_allocated_resource(acpi_handle, struct pnp_resource_table*); -acpi_status pnpacpi_parse_resource_option_data(acpi_handle, struct pnp_dev*); -int pnpacpi_encode_resources(struct pnp_resource_table *, struct acpi_buffer *); -int pnpacpi_build_resource_template(acpi_handle, struct acpi_buffer*); +int pnpacpi_parse_allocated_resource(struct pnp_dev *); +int pnpacpi_parse_resource_option_data(struct pnp_dev *); +int pnpacpi_encode_resources(struct pnp_dev *, struct acpi_buffer *); +int pnpacpi_build_resource_template(struct pnp_dev *, struct acpi_buffer *); #endif diff --git a/drivers/pnp/pnpacpi/rsparser.c b/drivers/pnp/pnpacpi/rsparser.c index 2dcd1960aca..66977ebf13b 100644 --- a/drivers/pnp/pnpacpi/rsparser.c +++ b/drivers/pnp/pnpacpi/rsparser.c @@ -3,6 +3,8 @@ * * Copyright (c) 2004 Matthieu Castet <castet.matthieu@free.fr> * Copyright (c) 2004 Li Shaohua <shaohua.li@intel.com> + * Copyright (C) 2008 Hewlett-Packard Development Company, L.P. + * Bjorn Helgaas <bjorn.helgaas@hp.com> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -21,35 +23,16 @@ #include <linux/kernel.h> #include <linux/acpi.h> #include <linux/pci.h> +#include <linux/pnp.h> +#include <linux/slab.h> +#include "../base.h" #include "pnpacpi.h" -#ifdef CONFIG_IA64 -#define valid_IRQ(i) (1) -#else -#define valid_IRQ(i) (((i) != 0) && ((i) != 2)) -#endif - -/* - * Allocated Resources - */ -static int irq_flags(int triggering, int polarity) +static void decode_irq_flags(struct pnp_dev *dev, int flags, int *triggering, + int *polarity, int *shareable) { - if (triggering == ACPI_LEVEL_SENSITIVE) { - if (polarity == ACPI_ACTIVE_LOW) - return IORESOURCE_IRQ_LOWLEVEL; - else - return IORESOURCE_IRQ_HIGHLEVEL; - } else { - if (polarity == ACPI_ACTIVE_LOW) - return IORESOURCE_IRQ_LOWEDGE; - else - return IORESOURCE_IRQ_HIGHEDGE; - } -} - -static void decode_irq_flags(int flag, int *triggering, int *polarity) -{ - switch (flag) { + switch (flags & (IORESOURCE_IRQ_LOWLEVEL | IORESOURCE_IRQ_HIGHLEVEL | + IORESOURCE_IRQ_LOWEDGE | IORESOURCE_IRQ_HIGHEDGE)) { case IORESOURCE_IRQ_LOWLEVEL: *triggering = ACPI_LEVEL_SENSITIVE; *polarity = ACPI_ACTIVE_LOW; @@ -66,64 +49,22 @@ static void decode_irq_flags(int flag, int *triggering, int *polarity) *triggering = ACPI_EDGE_SENSITIVE; *polarity = ACPI_ACTIVE_HIGH; break; + default: + dev_err(&dev->dev, "can't encode invalid IRQ mode %#x\n", + flags); + *triggering = ACPI_EDGE_SENSITIVE; + *polarity = ACPI_ACTIVE_HIGH; + break; } -} - -static void pnpacpi_parse_allocated_irqresource(struct pnp_resource_table *res, - u32 gsi, int triggering, - int polarity, int shareable) -{ - int i = 0; - int irq; - int p, t; - static unsigned char warned; - - if (!valid_IRQ(gsi)) - return; - - while (!(res->irq_resource[i].flags & IORESOURCE_UNSET) && - i < PNP_MAX_IRQ) - i++; - if (i >= PNP_MAX_IRQ && !warned) { - printk(KERN_WARNING "pnpacpi: exceeded the max number of IRQ " - "resources: %d \n", PNP_MAX_IRQ); - warned = 1; - return; - } - /* - * in IO-APIC mode, use overrided attribute. Two reasons: - * 1. BIOS bug in DSDT - * 2. BIOS uses IO-APIC mode Interrupt Source Override - */ - if (!acpi_get_override_irq(gsi, &t, &p)) { - t = t ? ACPI_LEVEL_SENSITIVE : ACPI_EDGE_SENSITIVE; - p = p ? ACPI_ACTIVE_LOW : ACPI_ACTIVE_HIGH; - - if (triggering != t || polarity != p) { - pnp_warn("IRQ %d override to %s, %s", - gsi, t ? "edge":"level", p ? "low":"high"); - triggering = t; - polarity = p; - } - } - - res->irq_resource[i].flags = IORESOURCE_IRQ; // Also clears _UNSET flag - res->irq_resource[i].flags |= irq_flags(triggering, polarity); - irq = acpi_register_gsi(gsi, triggering, polarity); - if (irq < 0) { - res->irq_resource[i].flags |= IORESOURCE_DISABLED; - return; - } - - if (shareable) - res->irq_resource[i].flags |= IORESOURCE_IRQ_SHAREABLE; - res->irq_resource[i].start = irq; - res->irq_resource[i].end = irq; - pcibios_penalize_isa_irq(irq, 1); + if (flags & IORESOURCE_IRQ_SHAREABLE) + *shareable = ACPI_SHARED; + else + *shareable = ACPI_EXCLUSIVE; } -static int dma_flags(int type, int bus_master, int transfer) +static int dma_flags(struct pnp_dev *dev, int type, int bus_master, + int transfer) { int flags = 0; @@ -145,7 +86,7 @@ static int dma_flags(int type, int bus_master, int transfer) default: /* Set a default value ? */ flags |= IORESOURCE_DMA_COMPATIBLE; - pnp_err("Invalid DMA type"); + dev_err(&dev->dev, "invalid DMA type %d\n", type); } switch (transfer) { case ACPI_TRANSFER_8: @@ -160,457 +101,363 @@ static int dma_flags(int type, int bus_master, int transfer) default: /* Set a default value ? */ flags |= IORESOURCE_DMA_8AND16BIT; - pnp_err("Invalid DMA transfer type"); + dev_err(&dev->dev, "invalid DMA transfer type %d\n", transfer); } return flags; } -static void pnpacpi_parse_allocated_dmaresource(struct pnp_resource_table *res, - u32 dma, int type, - int bus_master, int transfer) -{ - int i = 0; - static unsigned char warned; - - while (i < PNP_MAX_DMA && - !(res->dma_resource[i].flags & IORESOURCE_UNSET)) - i++; - if (i < PNP_MAX_DMA) { - res->dma_resource[i].flags = IORESOURCE_DMA; // Also clears _UNSET flag - res->dma_resource[i].flags |= - dma_flags(type, bus_master, transfer); - if (dma == -1) { - res->dma_resource[i].flags |= IORESOURCE_DISABLED; - return; - } - res->dma_resource[i].start = dma; - res->dma_resource[i].end = dma; - } else if (!warned) { - printk(KERN_WARNING "pnpacpi: exceeded the max number of DMA " - "resources: %d \n", PNP_MAX_DMA); - warned = 1; - } -} +/* + * Allocated Resources + */ -static void pnpacpi_parse_allocated_ioresource(struct pnp_resource_table *res, - u64 io, u64 len, int io_decode) +static void pnpacpi_add_irqresource(struct pnp_dev *dev, struct resource *r) { - int i = 0; - static unsigned char warned; + if (!(r->flags & IORESOURCE_DISABLED)) + pcibios_penalize_isa_irq(r->start, 1); - while (!(res->port_resource[i].flags & IORESOURCE_UNSET) && - i < PNP_MAX_PORT) - i++; - if (i < PNP_MAX_PORT) { - res->port_resource[i].flags = IORESOURCE_IO; // Also clears _UNSET flag - if (io_decode == ACPI_DECODE_16) - res->port_resource[i].flags |= PNP_PORT_FLAG_16BITADDR; - if (len <= 0 || (io + len - 1) >= 0x10003) { - res->port_resource[i].flags |= IORESOURCE_DISABLED; - return; - } - res->port_resource[i].start = io; - res->port_resource[i].end = io + len - 1; - } else if (!warned) { - printk(KERN_WARNING "pnpacpi: exceeded the max number of IO " - "resources: %d \n", PNP_MAX_PORT); - warned = 1; - } + pnp_add_resource(dev, r); } -static void pnpacpi_parse_allocated_memresource(struct pnp_resource_table *res, - u64 mem, u64 len, - int write_protect) -{ - int i = 0; - static unsigned char warned; +/* + * Device CSRs that do not appear in PCI config space should be described + * via ACPI. This would normally be done with Address Space Descriptors + * marked as "consumer-only," but old versions of Windows and Linux ignore + * the producer/consumer flag, so HP invented a vendor-defined resource to + * describe the location and size of CSR space. + */ +static struct acpi_vendor_uuid hp_ccsr_uuid = { + .subtype = 2, + .data = { 0xf9, 0xad, 0xe9, 0x69, 0x4f, 0x92, 0x5f, 0xab, 0xf6, 0x4a, + 0x24, 0xd2, 0x01, 0x37, 0x0e, 0xad }, +}; - while (!(res->mem_resource[i].flags & IORESOURCE_UNSET) && - (i < PNP_MAX_MEM)) - i++; - if (i < PNP_MAX_MEM) { - res->mem_resource[i].flags = IORESOURCE_MEM; // Also clears _UNSET flag - if (len <= 0) { - res->mem_resource[i].flags |= IORESOURCE_DISABLED; - return; +static int vendor_resource_matches(struct pnp_dev *dev, + struct acpi_resource_vendor_typed *vendor, + struct acpi_vendor_uuid *match, + int expected_len) +{ + int uuid_len = sizeof(vendor->uuid); + u8 uuid_subtype = vendor->uuid_subtype; + u8 *uuid = vendor->uuid; + int actual_len; + + /* byte_length includes uuid_subtype and uuid */ + actual_len = vendor->byte_length - uuid_len - 1; + + if (uuid_subtype == match->subtype && + uuid_len == sizeof(match->data) && + memcmp(uuid, match->data, uuid_len) == 0) { + if (expected_len && expected_len != actual_len) { + dev_err(&dev->dev, "wrong vendor descriptor size; " + "expected %d, found %d bytes\n", + expected_len, actual_len); + return 0; } - if (write_protect == ACPI_READ_WRITE_MEMORY) - res->mem_resource[i].flags |= IORESOURCE_MEM_WRITEABLE; - - res->mem_resource[i].start = mem; - res->mem_resource[i].end = mem + len - 1; - } else if (!warned) { - printk(KERN_WARNING "pnpacpi: exceeded the max number of mem " - "resources: %d\n", PNP_MAX_MEM); - warned = 1; + + return 1; } + + return 0; } -static void pnpacpi_parse_allocated_address_space(struct pnp_resource_table *res_table, - struct acpi_resource *res) +static void pnpacpi_parse_allocated_vendor(struct pnp_dev *dev, + struct acpi_resource_vendor_typed *vendor) { - struct acpi_resource_address64 addr, *p = &addr; - acpi_status status; + if (vendor_resource_matches(dev, vendor, &hp_ccsr_uuid, 16)) { + u64 start, length; - status = acpi_resource_to_address64(res, p); - if (!ACPI_SUCCESS(status)) { - pnp_warn("PnPACPI: failed to convert resource type %d", - res->type); - return; - } + memcpy(&start, vendor->byte_data, sizeof(start)); + memcpy(&length, vendor->byte_data + 8, sizeof(length)); - if (p->producer_consumer == ACPI_PRODUCER) - return; - - if (p->resource_type == ACPI_MEMORY_RANGE) - pnpacpi_parse_allocated_memresource(res_table, - p->minimum, p->address_length, - p->info.mem.write_protect); - else if (p->resource_type == ACPI_IO_RANGE) - pnpacpi_parse_allocated_ioresource(res_table, - p->minimum, p->address_length, - p->granularity == 0xfff ? ACPI_DECODE_10 : - ACPI_DECODE_16); + pnp_add_mem_resource(dev, start, start + length - 1, 0); + } } static acpi_status pnpacpi_allocated_resource(struct acpi_resource *res, void *data) { - struct pnp_resource_table *res_table = data; - int i; + struct pnp_dev *dev = data; + struct acpi_resource_dma *dma; + struct acpi_resource_vendor_typed *vendor_typed; + struct resource r = {0}; + int i, flags; - switch (res->type) { - case ACPI_RESOURCE_TYPE_IRQ: - /* - * Per spec, only one interrupt per descriptor is allowed in - * _CRS, but some firmware violates this, so parse them all. - */ - for (i = 0; i < res->data.irq.interrupt_count; i++) { - pnpacpi_parse_allocated_irqresource(res_table, - res->data.irq.interrupts[i], - res->data.irq.triggering, - res->data.irq.polarity, - res->data.irq.sharable); + if (acpi_dev_resource_address_space(res, &r) + || acpi_dev_resource_ext_address_space(res, &r)) { + pnp_add_resource(dev, &r); + return AE_OK; + } + + r.flags = 0; + if (acpi_dev_resource_interrupt(res, 0, &r)) { + pnpacpi_add_irqresource(dev, &r); + for (i = 1; acpi_dev_resource_interrupt(res, i, &r); i++) + pnpacpi_add_irqresource(dev, &r); + + if (i > 1) { + /* + * The IRQ encoder puts a single interrupt in each + * descriptor, so if a _CRS descriptor has more than + * one interrupt, we won't be able to re-encode it. + */ + if (pnp_can_write(dev)) { + dev_warn(&dev->dev, "multiple interrupts in " + "_CRS descriptor; configuration can't " + "be changed\n"); + dev->capabilities &= ~PNP_WRITE; + } } - break; + return AE_OK; + } else if (r.flags & IORESOURCE_DISABLED) { + pnp_add_irq_resource(dev, 0, IORESOURCE_DISABLED); + return AE_OK; + } - case ACPI_RESOURCE_TYPE_DMA: - if (res->data.dma.channel_count > 0) - pnpacpi_parse_allocated_dmaresource(res_table, - res->data.dma.channels[0], - res->data.dma.type, - res->data.dma.bus_master, - res->data.dma.transfer); + switch (res->type) { + case ACPI_RESOURCE_TYPE_MEMORY24: + case ACPI_RESOURCE_TYPE_MEMORY32: + case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: + if (acpi_dev_resource_memory(res, &r)) + pnp_add_resource(dev, &r); break; - case ACPI_RESOURCE_TYPE_IO: - pnpacpi_parse_allocated_ioresource(res_table, - res->data.io.minimum, - res->data.io.address_length, - res->data.io.io_decode); + case ACPI_RESOURCE_TYPE_FIXED_IO: + if (acpi_dev_resource_io(res, &r)) + pnp_add_resource(dev, &r); + break; + case ACPI_RESOURCE_TYPE_DMA: + dma = &res->data.dma; + if (dma->channel_count > 0 && dma->channels[0] != (u8) -1) + flags = dma_flags(dev, dma->type, dma->bus_master, + dma->transfer); + else + flags = IORESOURCE_DISABLED; + pnp_add_dma_resource(dev, dma->channels[0], flags); break; case ACPI_RESOURCE_TYPE_START_DEPENDENT: case ACPI_RESOURCE_TYPE_END_DEPENDENT: break; - case ACPI_RESOURCE_TYPE_FIXED_IO: - pnpacpi_parse_allocated_ioresource(res_table, - res->data.fixed_io.address, - res->data.fixed_io.address_length, - ACPI_DECODE_10); - break; - case ACPI_RESOURCE_TYPE_VENDOR: + vendor_typed = &res->data.vendor_typed; + pnpacpi_parse_allocated_vendor(dev, vendor_typed); break; case ACPI_RESOURCE_TYPE_END_TAG: break; - case ACPI_RESOURCE_TYPE_MEMORY24: - pnpacpi_parse_allocated_memresource(res_table, - res->data.memory24.minimum, - res->data.memory24.address_length, - res->data.memory24.write_protect); - break; - case ACPI_RESOURCE_TYPE_MEMORY32: - pnpacpi_parse_allocated_memresource(res_table, - res->data.memory32.minimum, - res->data.memory32.address_length, - res->data.memory32.write_protect); - break; - case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: - pnpacpi_parse_allocated_memresource(res_table, - res->data.fixed_memory32.address, - res->data.fixed_memory32.address_length, - res->data.fixed_memory32.write_protect); - break; - case ACPI_RESOURCE_TYPE_ADDRESS16: - case ACPI_RESOURCE_TYPE_ADDRESS32: - case ACPI_RESOURCE_TYPE_ADDRESS64: - pnpacpi_parse_allocated_address_space(res_table, res); - break; - - case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64: - if (res->data.ext_address64.producer_consumer == ACPI_PRODUCER) - return AE_OK; - break; - - case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: - if (res->data.extended_irq.producer_consumer == ACPI_PRODUCER) - return AE_OK; - - for (i = 0; i < res->data.extended_irq.interrupt_count; i++) { - pnpacpi_parse_allocated_irqresource(res_table, - res->data.extended_irq.interrupts[i], - res->data.extended_irq.triggering, - res->data.extended_irq.polarity, - res->data.extended_irq.sharable); - } - break; - case ACPI_RESOURCE_TYPE_GENERIC_REGISTER: break; default: - pnp_warn("PnPACPI: unknown resource type %d", res->type); + dev_warn(&dev->dev, "unknown resource type %d in _CRS\n", + res->type); return AE_ERROR; } return AE_OK; } -acpi_status pnpacpi_parse_allocated_resource(acpi_handle handle, - struct pnp_resource_table * res) +int pnpacpi_parse_allocated_resource(struct pnp_dev *dev) { - /* Blank the resource table values */ - pnp_init_resource_table(res); + struct acpi_device *acpi_dev = dev->data; + acpi_handle handle = acpi_dev->handle; + acpi_status status; + + pnp_dbg(&dev->dev, "parse allocated resources\n"); - return acpi_walk_resources(handle, METHOD_NAME__CRS, - pnpacpi_allocated_resource, res); + pnp_init_resources(dev); + + status = acpi_walk_resources(handle, METHOD_NAME__CRS, + pnpacpi_allocated_resource, dev); + + if (ACPI_FAILURE(status)) { + if (status != AE_NOT_FOUND) + dev_err(&dev->dev, "can't evaluate _CRS: %d", status); + return -EPERM; + } + return 0; } -static __init void pnpacpi_parse_dma_option(struct pnp_option *option, +static __init void pnpacpi_parse_dma_option(struct pnp_dev *dev, + unsigned int option_flags, struct acpi_resource_dma *p) { int i; - struct pnp_dma *dma; - - if (p->channel_count == 0) - return; - dma = kzalloc(sizeof(struct pnp_dma), GFP_KERNEL); - if (!dma) - return; + unsigned char map = 0, flags; for (i = 0; i < p->channel_count; i++) - dma->map |= 1 << p->channels[i]; + map |= 1 << p->channels[i]; - dma->flags = dma_flags(p->type, p->bus_master, p->transfer); - - pnp_register_dma_resource(option, dma); + flags = dma_flags(dev, p->type, p->bus_master, p->transfer); + pnp_register_dma_resource(dev, option_flags, map, flags); } -static __init void pnpacpi_parse_irq_option(struct pnp_option *option, +static __init void pnpacpi_parse_irq_option(struct pnp_dev *dev, + unsigned int option_flags, struct acpi_resource_irq *p) { int i; - struct pnp_irq *irq; - - if (p->interrupt_count == 0) - return; - irq = kzalloc(sizeof(struct pnp_irq), GFP_KERNEL); - if (!irq) - return; + pnp_irq_mask_t map; + unsigned char flags; + bitmap_zero(map.bits, PNP_IRQ_NR); for (i = 0; i < p->interrupt_count; i++) if (p->interrupts[i]) - __set_bit(p->interrupts[i], irq->map); - irq->flags = irq_flags(p->triggering, p->polarity); + __set_bit(p->interrupts[i], map.bits); - pnp_register_irq_resource(option, irq); + flags = acpi_dev_irq_flags(p->triggering, p->polarity, p->sharable); + pnp_register_irq_resource(dev, option_flags, &map, flags); } -static __init void pnpacpi_parse_ext_irq_option(struct pnp_option *option, +static __init void pnpacpi_parse_ext_irq_option(struct pnp_dev *dev, + unsigned int option_flags, struct acpi_resource_extended_irq *p) { int i; - struct pnp_irq *irq; - - if (p->interrupt_count == 0) - return; - irq = kzalloc(sizeof(struct pnp_irq), GFP_KERNEL); - if (!irq) - return; - - for (i = 0; i < p->interrupt_count; i++) - if (p->interrupts[i]) - __set_bit(p->interrupts[i], irq->map); - irq->flags = irq_flags(p->triggering, p->polarity); + pnp_irq_mask_t map; + unsigned char flags; + + bitmap_zero(map.bits, PNP_IRQ_NR); + for (i = 0; i < p->interrupt_count; i++) { + if (p->interrupts[i]) { + if (p->interrupts[i] < PNP_IRQ_NR) + __set_bit(p->interrupts[i], map.bits); + else + dev_err(&dev->dev, "ignoring IRQ %d option " + "(too large for %d entry bitmap)\n", + p->interrupts[i], PNP_IRQ_NR); + } + } - pnp_register_irq_resource(option, irq); + flags = acpi_dev_irq_flags(p->triggering, p->polarity, p->sharable); + pnp_register_irq_resource(dev, option_flags, &map, flags); } -static __init void pnpacpi_parse_port_option(struct pnp_option *option, +static __init void pnpacpi_parse_port_option(struct pnp_dev *dev, + unsigned int option_flags, struct acpi_resource_io *io) { - struct pnp_port *port; + unsigned char flags = 0; - if (io->address_length == 0) - return; - port = kzalloc(sizeof(struct pnp_port), GFP_KERNEL); - if (!port) - return; - port->min = io->minimum; - port->max = io->maximum; - port->align = io->alignment; - port->size = io->address_length; - port->flags = ACPI_DECODE_16 == io->io_decode ? - PNP_PORT_FLAG_16BITADDR : 0; - pnp_register_port_resource(option, port); + if (io->io_decode == ACPI_DECODE_16) + flags = IORESOURCE_IO_16BIT_ADDR; + pnp_register_port_resource(dev, option_flags, io->minimum, io->maximum, + io->alignment, io->address_length, flags); } -static __init void pnpacpi_parse_fixed_port_option(struct pnp_option *option, +static __init void pnpacpi_parse_fixed_port_option(struct pnp_dev *dev, + unsigned int option_flags, struct acpi_resource_fixed_io *io) { - struct pnp_port *port; - - if (io->address_length == 0) - return; - port = kzalloc(sizeof(struct pnp_port), GFP_KERNEL); - if (!port) - return; - port->min = port->max = io->address; - port->size = io->address_length; - port->align = 0; - port->flags = PNP_PORT_FLAG_FIXED; - pnp_register_port_resource(option, port); + pnp_register_port_resource(dev, option_flags, io->address, io->address, + 0, io->address_length, IORESOURCE_IO_FIXED); } -static __init void pnpacpi_parse_mem24_option(struct pnp_option *option, +static __init void pnpacpi_parse_mem24_option(struct pnp_dev *dev, + unsigned int option_flags, struct acpi_resource_memory24 *p) { - struct pnp_mem *mem; - - if (p->address_length == 0) - return; - mem = kzalloc(sizeof(struct pnp_mem), GFP_KERNEL); - if (!mem) - return; - mem->min = p->minimum; - mem->max = p->maximum; - mem->align = p->alignment; - mem->size = p->address_length; - - mem->flags = (ACPI_READ_WRITE_MEMORY == p->write_protect) ? - IORESOURCE_MEM_WRITEABLE : 0; + unsigned char flags = 0; - pnp_register_mem_resource(option, mem); + if (p->write_protect == ACPI_READ_WRITE_MEMORY) + flags = IORESOURCE_MEM_WRITEABLE; + pnp_register_mem_resource(dev, option_flags, p->minimum, p->maximum, + p->alignment, p->address_length, flags); } -static __init void pnpacpi_parse_mem32_option(struct pnp_option *option, +static __init void pnpacpi_parse_mem32_option(struct pnp_dev *dev, + unsigned int option_flags, struct acpi_resource_memory32 *p) { - struct pnp_mem *mem; + unsigned char flags = 0; - if (p->address_length == 0) - return; - mem = kzalloc(sizeof(struct pnp_mem), GFP_KERNEL); - if (!mem) - return; - mem->min = p->minimum; - mem->max = p->maximum; - mem->align = p->alignment; - mem->size = p->address_length; - - mem->flags = (ACPI_READ_WRITE_MEMORY == p->write_protect) ? - IORESOURCE_MEM_WRITEABLE : 0; - - pnp_register_mem_resource(option, mem); + if (p->write_protect == ACPI_READ_WRITE_MEMORY) + flags = IORESOURCE_MEM_WRITEABLE; + pnp_register_mem_resource(dev, option_flags, p->minimum, p->maximum, + p->alignment, p->address_length, flags); } -static __init void pnpacpi_parse_fixed_mem32_option(struct pnp_option *option, +static __init void pnpacpi_parse_fixed_mem32_option(struct pnp_dev *dev, + unsigned int option_flags, struct acpi_resource_fixed_memory32 *p) { - struct pnp_mem *mem; - - if (p->address_length == 0) - return; - mem = kzalloc(sizeof(struct pnp_mem), GFP_KERNEL); - if (!mem) - return; - mem->min = mem->max = p->address; - mem->size = p->address_length; - mem->align = 0; - - mem->flags = (ACPI_READ_WRITE_MEMORY == p->write_protect) ? - IORESOURCE_MEM_WRITEABLE : 0; + unsigned char flags = 0; - pnp_register_mem_resource(option, mem); + if (p->write_protect == ACPI_READ_WRITE_MEMORY) + flags = IORESOURCE_MEM_WRITEABLE; + pnp_register_mem_resource(dev, option_flags, p->address, p->address, + 0, p->address_length, flags); } -static __init void pnpacpi_parse_address_option(struct pnp_option *option, +static __init void pnpacpi_parse_address_option(struct pnp_dev *dev, + unsigned int option_flags, struct acpi_resource *r) { struct acpi_resource_address64 addr, *p = &addr; acpi_status status; - struct pnp_mem *mem; - struct pnp_port *port; + unsigned char flags = 0; status = acpi_resource_to_address64(r, p); - if (!ACPI_SUCCESS(status)) { - pnp_warn("PnPACPI: failed to convert resource type %d", + if (ACPI_FAILURE(status)) { + dev_warn(&dev->dev, "can't convert resource type %d\n", r->type); return; } - if (p->address_length == 0) - return; + if (p->resource_type == ACPI_MEMORY_RANGE) { + if (p->info.mem.write_protect == ACPI_READ_WRITE_MEMORY) + flags = IORESOURCE_MEM_WRITEABLE; + pnp_register_mem_resource(dev, option_flags, p->minimum, + p->minimum, 0, p->address_length, + flags); + } else if (p->resource_type == ACPI_IO_RANGE) + pnp_register_port_resource(dev, option_flags, p->minimum, + p->minimum, 0, p->address_length, + IORESOURCE_IO_FIXED); +} + +static __init void pnpacpi_parse_ext_address_option(struct pnp_dev *dev, + unsigned int option_flags, + struct acpi_resource *r) +{ + struct acpi_resource_extended_address64 *p = &r->data.ext_address64; + unsigned char flags = 0; if (p->resource_type == ACPI_MEMORY_RANGE) { - mem = kzalloc(sizeof(struct pnp_mem), GFP_KERNEL); - if (!mem) - return; - mem->min = mem->max = p->minimum; - mem->size = p->address_length; - mem->align = 0; - mem->flags = (p->info.mem.write_protect == - ACPI_READ_WRITE_MEMORY) ? IORESOURCE_MEM_WRITEABLE - : 0; - pnp_register_mem_resource(option, mem); - } else if (p->resource_type == ACPI_IO_RANGE) { - port = kzalloc(sizeof(struct pnp_port), GFP_KERNEL); - if (!port) - return; - port->min = port->max = p->minimum; - port->size = p->address_length; - port->align = 0; - port->flags = PNP_PORT_FLAG_FIXED; - pnp_register_port_resource(option, port); - } + if (p->info.mem.write_protect == ACPI_READ_WRITE_MEMORY) + flags = IORESOURCE_MEM_WRITEABLE; + pnp_register_mem_resource(dev, option_flags, p->minimum, + p->minimum, 0, p->address_length, + flags); + } else if (p->resource_type == ACPI_IO_RANGE) + pnp_register_port_resource(dev, option_flags, p->minimum, + p->minimum, 0, p->address_length, + IORESOURCE_IO_FIXED); } struct acpipnp_parse_option_s { - struct pnp_option *option; - struct pnp_option *option_independent; struct pnp_dev *dev; + unsigned int option_flags; }; static __init acpi_status pnpacpi_option_resource(struct acpi_resource *res, void *data) { - int priority = 0; + int priority; struct acpipnp_parse_option_s *parse_data = data; struct pnp_dev *dev = parse_data->dev; - struct pnp_option *option = parse_data->option; + unsigned int option_flags = parse_data->option_flags; switch (res->type) { case ACPI_RESOURCE_TYPE_IRQ: - pnpacpi_parse_irq_option(option, &res->data.irq); + pnpacpi_parse_irq_option(dev, option_flags, &res->data.irq); break; case ACPI_RESOURCE_TYPE_DMA: - pnpacpi_parse_dma_option(option, &res->data.dma); + pnpacpi_parse_dma_option(dev, option_flags, &res->data.dma); break; case ACPI_RESOURCE_TYPE_START_DEPENDENT: @@ -630,29 +477,20 @@ static __init acpi_status pnpacpi_option_resource(struct acpi_resource *res, priority = PNP_RES_PRIORITY_INVALID; break; } - /* TBD: Consider performance/robustness bits */ - option = pnp_register_dependent_option(dev, priority); - if (!option) - return AE_ERROR; - parse_data->option = option; + parse_data->option_flags = pnp_new_dependent_set(dev, priority); break; case ACPI_RESOURCE_TYPE_END_DEPENDENT: - /*only one EndDependentFn is allowed */ - if (!parse_data->option_independent) { - pnp_warn("PnPACPI: more than one EndDependentFn"); - return AE_ERROR; - } - parse_data->option = parse_data->option_independent; - parse_data->option_independent = NULL; + parse_data->option_flags = 0; break; case ACPI_RESOURCE_TYPE_IO: - pnpacpi_parse_port_option(option, &res->data.io); + pnpacpi_parse_port_option(dev, option_flags, &res->data.io); break; case ACPI_RESOURCE_TYPE_FIXED_IO: - pnpacpi_parse_fixed_port_option(option, &res->data.fixed_io); + pnpacpi_parse_fixed_port_option(dev, option_flags, + &res->data.fixed_io); break; case ACPI_RESOURCE_TYPE_VENDOR: @@ -660,57 +498,68 @@ static __init acpi_status pnpacpi_option_resource(struct acpi_resource *res, break; case ACPI_RESOURCE_TYPE_MEMORY24: - pnpacpi_parse_mem24_option(option, &res->data.memory24); + pnpacpi_parse_mem24_option(dev, option_flags, + &res->data.memory24); break; case ACPI_RESOURCE_TYPE_MEMORY32: - pnpacpi_parse_mem32_option(option, &res->data.memory32); + pnpacpi_parse_mem32_option(dev, option_flags, + &res->data.memory32); break; case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: - pnpacpi_parse_fixed_mem32_option(option, + pnpacpi_parse_fixed_mem32_option(dev, option_flags, &res->data.fixed_memory32); break; case ACPI_RESOURCE_TYPE_ADDRESS16: case ACPI_RESOURCE_TYPE_ADDRESS32: case ACPI_RESOURCE_TYPE_ADDRESS64: - pnpacpi_parse_address_option(option, res); + pnpacpi_parse_address_option(dev, option_flags, res); break; case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64: + pnpacpi_parse_ext_address_option(dev, option_flags, res); break; case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: - pnpacpi_parse_ext_irq_option(option, &res->data.extended_irq); + pnpacpi_parse_ext_irq_option(dev, option_flags, + &res->data.extended_irq); break; case ACPI_RESOURCE_TYPE_GENERIC_REGISTER: break; default: - pnp_warn("PnPACPI: unknown resource type %d", res->type); + dev_warn(&dev->dev, "unknown resource type %d in _PRS\n", + res->type); return AE_ERROR; } return AE_OK; } -acpi_status __init pnpacpi_parse_resource_option_data(acpi_handle handle, - struct pnp_dev *dev) +int __init pnpacpi_parse_resource_option_data(struct pnp_dev *dev) { + struct acpi_device *acpi_dev = dev->data; + acpi_handle handle = acpi_dev->handle; acpi_status status; struct acpipnp_parse_option_s parse_data; - parse_data.option = pnp_register_independent_option(dev); - if (!parse_data.option) - return AE_ERROR; - parse_data.option_independent = parse_data.option; + pnp_dbg(&dev->dev, "parse resource options\n"); + parse_data.dev = dev; + parse_data.option_flags = 0; + status = acpi_walk_resources(handle, METHOD_NAME__PRS, pnpacpi_option_resource, &parse_data); - return status; + if (ACPI_FAILURE(status)) { + if (status != AE_NOT_FOUND) + dev_err(&dev->dev, "can't evaluate _PRS: %d", status); + return -EPERM; + } + return 0; } static int pnpacpi_supported_resource(struct acpi_resource *res) @@ -726,6 +575,7 @@ static int pnpacpi_supported_resource(struct acpi_resource *res) case ACPI_RESOURCE_TYPE_ADDRESS16: case ACPI_RESOURCE_TYPE_ADDRESS32: case ACPI_RESOURCE_TYPE_ADDRESS64: + case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64: case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: return 1; } @@ -752,15 +602,20 @@ static acpi_status pnpacpi_type_resources(struct acpi_resource *res, void *data) if (pnpacpi_supported_resource(res)) { (*resource)->type = res->type; (*resource)->length = sizeof(struct acpi_resource); + if (res->type == ACPI_RESOURCE_TYPE_IRQ) + (*resource)->data.irq.descriptor_length = + res->data.irq.descriptor_length; (*resource)++; } return AE_OK; } -int pnpacpi_build_resource_template(acpi_handle handle, +int pnpacpi_build_resource_template(struct pnp_dev *dev, struct acpi_buffer *buffer) { + struct acpi_device *acpi_dev = dev->data; + acpi_handle handle = acpi_dev->handle; struct acpi_resource *resource; int res_cnt = 0; acpi_status status; @@ -768,7 +623,7 @@ int pnpacpi_build_resource_template(acpi_handle handle, status = acpi_walk_resources(handle, METHOD_NAME__CRS, pnpacpi_count_resources, &res_cnt); if (ACPI_FAILURE(status)) { - pnp_err("Evaluate _CRS failed"); + dev_err(&dev->dev, "can't evaluate _CRS: %d\n", status); return -EINVAL; } if (!res_cnt) @@ -777,144 +632,241 @@ int pnpacpi_build_resource_template(acpi_handle handle, buffer->pointer = kzalloc(buffer->length - 1, GFP_KERNEL); if (!buffer->pointer) return -ENOMEM; - pnp_dbg("Res cnt %d", res_cnt); + resource = (struct acpi_resource *)buffer->pointer; status = acpi_walk_resources(handle, METHOD_NAME__CRS, pnpacpi_type_resources, &resource); if (ACPI_FAILURE(status)) { kfree(buffer->pointer); - pnp_err("Evaluate _CRS failed"); + dev_err(&dev->dev, "can't evaluate _CRS: %d\n", status); return -EINVAL; } /* resource will pointer the end resource now */ resource->type = ACPI_RESOURCE_TYPE_END_TAG; + resource->length = sizeof(struct acpi_resource); return 0; } -static void pnpacpi_encode_irq(struct acpi_resource *resource, +static void pnpacpi_encode_irq(struct pnp_dev *dev, + struct acpi_resource *resource, struct resource *p) { - int triggering, polarity; + struct acpi_resource_irq *irq = &resource->data.irq; + int triggering, polarity, shareable; - decode_irq_flags(p->flags & IORESOURCE_BITS, &triggering, &polarity); - resource->data.irq.triggering = triggering; - resource->data.irq.polarity = polarity; - if (triggering == ACPI_EDGE_SENSITIVE) - resource->data.irq.sharable = ACPI_EXCLUSIVE; - else - resource->data.irq.sharable = ACPI_SHARED; - resource->data.irq.interrupt_count = 1; - resource->data.irq.interrupts[0] = p->start; + if (!pnp_resource_enabled(p)) { + irq->interrupt_count = 0; + pnp_dbg(&dev->dev, " encode irq (%s)\n", + p ? "disabled" : "missing"); + return; + } + + decode_irq_flags(dev, p->flags, &triggering, &polarity, &shareable); + irq->triggering = triggering; + irq->polarity = polarity; + irq->sharable = shareable; + irq->interrupt_count = 1; + irq->interrupts[0] = p->start; + + pnp_dbg(&dev->dev, " encode irq %d %s %s %s (%d-byte descriptor)\n", + (int) p->start, + triggering == ACPI_LEVEL_SENSITIVE ? "level" : "edge", + polarity == ACPI_ACTIVE_LOW ? "low" : "high", + irq->sharable == ACPI_SHARED ? "shared" : "exclusive", + irq->descriptor_length); } -static void pnpacpi_encode_ext_irq(struct acpi_resource *resource, +static void pnpacpi_encode_ext_irq(struct pnp_dev *dev, + struct acpi_resource *resource, struct resource *p) { - int triggering, polarity; + struct acpi_resource_extended_irq *extended_irq = &resource->data.extended_irq; + int triggering, polarity, shareable; - decode_irq_flags(p->flags & IORESOURCE_BITS, &triggering, &polarity); - resource->data.extended_irq.producer_consumer = ACPI_CONSUMER; - resource->data.extended_irq.triggering = triggering; - resource->data.extended_irq.polarity = polarity; - if (triggering == ACPI_EDGE_SENSITIVE) - resource->data.irq.sharable = ACPI_EXCLUSIVE; - else - resource->data.irq.sharable = ACPI_SHARED; - resource->data.extended_irq.interrupt_count = 1; - resource->data.extended_irq.interrupts[0] = p->start; + if (!pnp_resource_enabled(p)) { + extended_irq->interrupt_count = 0; + pnp_dbg(&dev->dev, " encode extended irq (%s)\n", + p ? "disabled" : "missing"); + return; + } + + decode_irq_flags(dev, p->flags, &triggering, &polarity, &shareable); + extended_irq->producer_consumer = ACPI_CONSUMER; + extended_irq->triggering = triggering; + extended_irq->polarity = polarity; + extended_irq->sharable = shareable; + extended_irq->interrupt_count = 1; + extended_irq->interrupts[0] = p->start; + + pnp_dbg(&dev->dev, " encode irq %d %s %s %s\n", (int) p->start, + triggering == ACPI_LEVEL_SENSITIVE ? "level" : "edge", + polarity == ACPI_ACTIVE_LOW ? "low" : "high", + extended_irq->sharable == ACPI_SHARED ? "shared" : "exclusive"); } -static void pnpacpi_encode_dma(struct acpi_resource *resource, +static void pnpacpi_encode_dma(struct pnp_dev *dev, + struct acpi_resource *resource, struct resource *p) { + struct acpi_resource_dma *dma = &resource->data.dma; + + if (!pnp_resource_enabled(p)) { + dma->channel_count = 0; + pnp_dbg(&dev->dev, " encode dma (%s)\n", + p ? "disabled" : "missing"); + return; + } + /* Note: pnp_assign_dma will copy pnp_dma->flags into p->flags */ switch (p->flags & IORESOURCE_DMA_SPEED_MASK) { case IORESOURCE_DMA_TYPEA: - resource->data.dma.type = ACPI_TYPE_A; + dma->type = ACPI_TYPE_A; break; case IORESOURCE_DMA_TYPEB: - resource->data.dma.type = ACPI_TYPE_B; + dma->type = ACPI_TYPE_B; break; case IORESOURCE_DMA_TYPEF: - resource->data.dma.type = ACPI_TYPE_F; + dma->type = ACPI_TYPE_F; break; default: - resource->data.dma.type = ACPI_COMPATIBILITY; + dma->type = ACPI_COMPATIBILITY; } switch (p->flags & IORESOURCE_DMA_TYPE_MASK) { case IORESOURCE_DMA_8BIT: - resource->data.dma.transfer = ACPI_TRANSFER_8; + dma->transfer = ACPI_TRANSFER_8; break; case IORESOURCE_DMA_8AND16BIT: - resource->data.dma.transfer = ACPI_TRANSFER_8_16; + dma->transfer = ACPI_TRANSFER_8_16; break; default: - resource->data.dma.transfer = ACPI_TRANSFER_16; + dma->transfer = ACPI_TRANSFER_16; } - resource->data.dma.bus_master = !!(p->flags & IORESOURCE_DMA_MASTER); - resource->data.dma.channel_count = 1; - resource->data.dma.channels[0] = p->start; + dma->bus_master = !!(p->flags & IORESOURCE_DMA_MASTER); + dma->channel_count = 1; + dma->channels[0] = p->start; + + pnp_dbg(&dev->dev, " encode dma %d " + "type %#x transfer %#x master %d\n", + (int) p->start, dma->type, dma->transfer, dma->bus_master); } -static void pnpacpi_encode_io(struct acpi_resource *resource, +static void pnpacpi_encode_io(struct pnp_dev *dev, + struct acpi_resource *resource, struct resource *p) { - /* Note: pnp_assign_port will copy pnp_port->flags into p->flags */ - resource->data.io.io_decode = (p->flags & PNP_PORT_FLAG_16BITADDR) ? - ACPI_DECODE_16 : ACPI_DECODE_10; - resource->data.io.minimum = p->start; - resource->data.io.maximum = p->end; - resource->data.io.alignment = 0; /* Correct? */ - resource->data.io.address_length = p->end - p->start + 1; + struct acpi_resource_io *io = &resource->data.io; + + if (pnp_resource_enabled(p)) { + /* Note: pnp_assign_port copies pnp_port->flags into p->flags */ + io->io_decode = (p->flags & IORESOURCE_IO_16BIT_ADDR) ? + ACPI_DECODE_16 : ACPI_DECODE_10; + io->minimum = p->start; + io->maximum = p->end; + io->alignment = 0; /* Correct? */ + io->address_length = resource_size(p); + } else { + io->minimum = 0; + io->address_length = 0; + } + + pnp_dbg(&dev->dev, " encode io %#x-%#x decode %#x\n", io->minimum, + io->minimum + io->address_length - 1, io->io_decode); } -static void pnpacpi_encode_fixed_io(struct acpi_resource *resource, +static void pnpacpi_encode_fixed_io(struct pnp_dev *dev, + struct acpi_resource *resource, struct resource *p) { - resource->data.fixed_io.address = p->start; - resource->data.fixed_io.address_length = p->end - p->start + 1; + struct acpi_resource_fixed_io *fixed_io = &resource->data.fixed_io; + + if (pnp_resource_enabled(p)) { + fixed_io->address = p->start; + fixed_io->address_length = resource_size(p); + } else { + fixed_io->address = 0; + fixed_io->address_length = 0; + } + + pnp_dbg(&dev->dev, " encode fixed_io %#x-%#x\n", fixed_io->address, + fixed_io->address + fixed_io->address_length - 1); } -static void pnpacpi_encode_mem24(struct acpi_resource *resource, +static void pnpacpi_encode_mem24(struct pnp_dev *dev, + struct acpi_resource *resource, struct resource *p) { - /* Note: pnp_assign_mem will copy pnp_mem->flags into p->flags */ - resource->data.memory24.write_protect = - (p->flags & IORESOURCE_MEM_WRITEABLE) ? - ACPI_READ_WRITE_MEMORY : ACPI_READ_ONLY_MEMORY; - resource->data.memory24.minimum = p->start; - resource->data.memory24.maximum = p->end; - resource->data.memory24.alignment = 0; - resource->data.memory24.address_length = p->end - p->start + 1; + struct acpi_resource_memory24 *memory24 = &resource->data.memory24; + + if (pnp_resource_enabled(p)) { + /* Note: pnp_assign_mem copies pnp_mem->flags into p->flags */ + memory24->write_protect = p->flags & IORESOURCE_MEM_WRITEABLE ? + ACPI_READ_WRITE_MEMORY : ACPI_READ_ONLY_MEMORY; + memory24->minimum = p->start; + memory24->maximum = p->end; + memory24->alignment = 0; + memory24->address_length = resource_size(p); + } else { + memory24->minimum = 0; + memory24->address_length = 0; + } + + pnp_dbg(&dev->dev, " encode mem24 %#x-%#x write_protect %#x\n", + memory24->minimum, + memory24->minimum + memory24->address_length - 1, + memory24->write_protect); } -static void pnpacpi_encode_mem32(struct acpi_resource *resource, +static void pnpacpi_encode_mem32(struct pnp_dev *dev, + struct acpi_resource *resource, struct resource *p) { - resource->data.memory32.write_protect = - (p->flags & IORESOURCE_MEM_WRITEABLE) ? - ACPI_READ_WRITE_MEMORY : ACPI_READ_ONLY_MEMORY; - resource->data.memory32.minimum = p->start; - resource->data.memory32.maximum = p->end; - resource->data.memory32.alignment = 0; - resource->data.memory32.address_length = p->end - p->start + 1; + struct acpi_resource_memory32 *memory32 = &resource->data.memory32; + + if (pnp_resource_enabled(p)) { + memory32->write_protect = p->flags & IORESOURCE_MEM_WRITEABLE ? + ACPI_READ_WRITE_MEMORY : ACPI_READ_ONLY_MEMORY; + memory32->minimum = p->start; + memory32->maximum = p->end; + memory32->alignment = 0; + memory32->address_length = resource_size(p); + } else { + memory32->minimum = 0; + memory32->alignment = 0; + } + + pnp_dbg(&dev->dev, " encode mem32 %#x-%#x write_protect %#x\n", + memory32->minimum, + memory32->minimum + memory32->address_length - 1, + memory32->write_protect); } -static void pnpacpi_encode_fixed_mem32(struct acpi_resource *resource, +static void pnpacpi_encode_fixed_mem32(struct pnp_dev *dev, + struct acpi_resource *resource, struct resource *p) { - resource->data.fixed_memory32.write_protect = - (p->flags & IORESOURCE_MEM_WRITEABLE) ? - ACPI_READ_WRITE_MEMORY : ACPI_READ_ONLY_MEMORY; - resource->data.fixed_memory32.address = p->start; - resource->data.fixed_memory32.address_length = p->end - p->start + 1; + struct acpi_resource_fixed_memory32 *fixed_memory32 = &resource->data.fixed_memory32; + + if (pnp_resource_enabled(p)) { + fixed_memory32->write_protect = + p->flags & IORESOURCE_MEM_WRITEABLE ? + ACPI_READ_WRITE_MEMORY : ACPI_READ_ONLY_MEMORY; + fixed_memory32->address = p->start; + fixed_memory32->address_length = resource_size(p); + } else { + fixed_memory32->address = 0; + fixed_memory32->address_length = 0; + } + + pnp_dbg(&dev->dev, " encode fixed_mem32 %#x-%#x write_protect %#x\n", + fixed_memory32->address, + fixed_memory32->address + fixed_memory32->address_length - 1, + fixed_memory32->write_protect); } -int pnpacpi_encode_resources(struct pnp_resource_table *res_table, - struct acpi_buffer *buffer) +int pnpacpi_encode_resources(struct pnp_dev *dev, struct acpi_buffer *buffer) { int i = 0; /* pnpacpi_build_resource_template allocates extra mem */ @@ -922,58 +874,48 @@ int pnpacpi_encode_resources(struct pnp_resource_table *res_table, struct acpi_resource *resource = buffer->pointer; int port = 0, irq = 0, dma = 0, mem = 0; - pnp_dbg("res cnt %d", res_cnt); + pnp_dbg(&dev->dev, "encode %d resources\n", res_cnt); while (i < res_cnt) { switch (resource->type) { case ACPI_RESOURCE_TYPE_IRQ: - pnp_dbg("Encode irq"); - pnpacpi_encode_irq(resource, - &res_table->irq_resource[irq]); + pnpacpi_encode_irq(dev, resource, + pnp_get_resource(dev, IORESOURCE_IRQ, irq)); irq++; break; case ACPI_RESOURCE_TYPE_DMA: - pnp_dbg("Encode dma"); - pnpacpi_encode_dma(resource, - &res_table->dma_resource[dma]); + pnpacpi_encode_dma(dev, resource, + pnp_get_resource(dev, IORESOURCE_DMA, dma)); dma++; break; case ACPI_RESOURCE_TYPE_IO: - pnp_dbg("Encode io"); - pnpacpi_encode_io(resource, - &res_table->port_resource[port]); + pnpacpi_encode_io(dev, resource, + pnp_get_resource(dev, IORESOURCE_IO, port)); port++; break; case ACPI_RESOURCE_TYPE_FIXED_IO: - pnp_dbg("Encode fixed io"); - pnpacpi_encode_fixed_io(resource, - &res_table-> - port_resource[port]); + pnpacpi_encode_fixed_io(dev, resource, + pnp_get_resource(dev, IORESOURCE_IO, port)); port++; break; case ACPI_RESOURCE_TYPE_MEMORY24: - pnp_dbg("Encode mem24"); - pnpacpi_encode_mem24(resource, - &res_table->mem_resource[mem]); + pnpacpi_encode_mem24(dev, resource, + pnp_get_resource(dev, IORESOURCE_MEM, mem)); mem++; break; case ACPI_RESOURCE_TYPE_MEMORY32: - pnp_dbg("Encode mem32"); - pnpacpi_encode_mem32(resource, - &res_table->mem_resource[mem]); + pnpacpi_encode_mem32(dev, resource, + pnp_get_resource(dev, IORESOURCE_MEM, mem)); mem++; break; case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: - pnp_dbg("Encode fixed mem32"); - pnpacpi_encode_fixed_mem32(resource, - &res_table-> - mem_resource[mem]); + pnpacpi_encode_fixed_mem32(dev, resource, + pnp_get_resource(dev, IORESOURCE_MEM, mem)); mem++; break; case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: - pnp_dbg("Encode ext irq"); - pnpacpi_encode_ext_irq(resource, - &res_table->irq_resource[irq]); + pnpacpi_encode_ext_irq(dev, resource, + pnp_get_resource(dev, IORESOURCE_IRQ, irq)); irq++; break; case ACPI_RESOURCE_TYPE_START_DEPENDENT: @@ -986,7 +928,8 @@ int pnpacpi_encode_resources(struct pnp_resource_table *res_table, case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64: case ACPI_RESOURCE_TYPE_GENERIC_REGISTER: default: /* other type */ - pnp_warn("unknown resource type %d", resource->type); + dev_warn(&dev->dev, "can't encode unknown resource " + "type %d\n", resource->type); return -EINVAL; } resource++; diff --git a/drivers/pnp/pnpbios/Kconfig b/drivers/pnp/pnpbios/Kconfig index b986d9fa3b9..50c3dd065e0 100644 --- a/drivers/pnp/pnpbios/Kconfig +++ b/drivers/pnp/pnpbios/Kconfig @@ -2,8 +2,8 @@ # Plug and Play BIOS configuration # config PNPBIOS - bool "Plug and Play BIOS support (EXPERIMENTAL)" - depends on ISA && X86 && EXPERIMENTAL + bool "Plug and Play BIOS support" + depends on ISA && X86 default n ---help--- Linux uses the PNPBIOS as defined in "Plug and Play BIOS diff --git a/drivers/pnp/pnpbios/Makefile b/drivers/pnp/pnpbios/Makefile index 3cd3ed76060..240b0ffb83c 100644 --- a/drivers/pnp/pnpbios/Makefile +++ b/drivers/pnp/pnpbios/Makefile @@ -1,7 +1,8 @@ # # Makefile for the kernel PNPBIOS driver. # +obj-y := pnp.o -pnpbios-proc-$(CONFIG_PNPBIOS_PROC_FS) = proc.o +pnp-y := core.o bioscalls.o rsparser.o -obj-y := core.o bioscalls.o rsparser.o $(pnpbios-proc-y) +pnp-$(CONFIG_PNPBIOS_PROC_FS) += proc.o diff --git a/drivers/pnp/pnpbios/bioscalls.c b/drivers/pnp/pnpbios/bioscalls.c index a8364d81522..438d4c72c7b 100644 --- a/drivers/pnp/pnpbios/bioscalls.c +++ b/drivers/pnp/pnpbios/bioscalls.c @@ -7,24 +7,21 @@ #include <linux/init.h> #include <linux/linkage.h> #include <linux/kernel.h> -#include <linux/pnpbios.h> #include <linux/device.h> #include <linux/pnp.h> #include <linux/mm.h> #include <linux/smp.h> -#include <linux/slab.h> #include <linux/kmod.h> #include <linux/completion.h> #include <linux/spinlock.h> #include <asm/page.h> #include <asm/desc.h> -#include <asm/system.h> #include <asm/byteorder.h> #include "pnpbios.h" -static struct { +__visible struct { u16 offset; u16 segment; } pnp_bios_callpoint; @@ -40,10 +37,11 @@ static struct { * kernel begins at offset 3GB... */ -asmlinkage void pnp_bios_callfunc(void); +asmlinkage __visible void pnp_bios_callfunc(void); __asm__(".text \n" __ALIGN_STR "\n" + ".globl pnp_bios_callfunc\n" "pnp_bios_callfunc:\n" " pushl %edx \n" " pushl %ecx \n" @@ -56,21 +54,22 @@ __asm__(".text \n" #define Q2_SET_SEL(cpu, selname, address, size) \ do { \ -struct desc_struct *gdt = get_cpu_gdt_table((cpu)); \ -set_base(gdt[(selname) >> 3], (u32)(address)); \ -set_limit(gdt[(selname) >> 3], size); \ + struct desc_struct *gdt = get_cpu_gdt_table((cpu)); \ + set_desc_base(&gdt[(selname) >> 3], (u32)(address)); \ + set_desc_limit(&gdt[(selname) >> 3], (size) - 1); \ } while(0) -static struct desc_struct bad_bios_desc; +static struct desc_struct bad_bios_desc = GDT_ENTRY_INIT(0x4092, + (unsigned long)__va(0x400UL), PAGE_SIZE - 0x400 - 1); /* * At some point we want to use this stack frame pointer to unwind * after PnP BIOS oopses. */ -u32 pnp_bios_fault_esp; -u32 pnp_bios_fault_eip; -u32 pnp_bios_is_utter_crap = 0; +__visible u32 pnp_bios_fault_esp; +__visible u32 pnp_bios_fault_eip; +__visible u32 pnp_bios_is_utter_crap = 0; static spinlock_t pnp_bios_lock; @@ -220,7 +219,7 @@ void pnpbios_print_status(const char *module, u16 status) module); break; case PNP_HARDWARE_ERROR: - printk(KERN_ERR "PnPBIOS: %s: a hardware failure has occured\n", + printk(KERN_ERR "PnPBIOS: %s: a hardware failure has occurred\n", module); break; default: @@ -477,19 +476,15 @@ void pnpbios_calls_init(union pnp_bios_install_struct *header) pnp_bios_callpoint.offset = header->fields.pm16offset; pnp_bios_callpoint.segment = PNP_CS16; - bad_bios_desc.a = 0; - bad_bios_desc.b = 0x00409200; - - set_base(bad_bios_desc, __va((unsigned long)0x40 << 4)); - _set_limit((char *)&bad_bios_desc, 4095 - (0x40 << 4)); - for (i = 0; i < NR_CPUS; i++) { + for_each_possible_cpu(i) { struct desc_struct *gdt = get_cpu_gdt_table(i); if (!gdt) continue; - set_base(gdt[GDT_ENTRY_PNPBIOS_CS32], &pnp_bios_callfunc); - set_base(gdt[GDT_ENTRY_PNPBIOS_CS16], - __va(header->fields.pm16cseg)); - set_base(gdt[GDT_ENTRY_PNPBIOS_DS], - __va(header->fields.pm16dseg)); + set_desc_base(&gdt[GDT_ENTRY_PNPBIOS_CS32], + (unsigned long)&pnp_bios_callfunc); + set_desc_base(&gdt[GDT_ENTRY_PNPBIOS_CS16], + (unsigned long)__va(header->fields.pm16cseg)); + set_desc_base(&gdt[GDT_ENTRY_PNPBIOS_DS], + (unsigned long)__va(header->fields.pm16dseg)); } } diff --git a/drivers/pnp/pnpbios/core.c b/drivers/pnp/pnpbios/core.c index a8a51500e1e..074569e77d2 100644 --- a/drivers/pnp/pnpbios/core.c +++ b/drivers/pnp/pnpbios/core.c @@ -50,7 +50,6 @@ #include <linux/init.h> #include <linux/linkage.h> #include <linux/kernel.h> -#include <linux/pnpbios.h> #include <linux/device.h> #include <linux/pnp.h> #include <linux/mm.h> @@ -66,9 +65,9 @@ #include <asm/page.h> #include <asm/desc.h> -#include <asm/system.h> #include <asm/byteorder.h> +#include "../base.h" #include "pnpbios.h" /* @@ -92,9 +91,6 @@ struct pnp_dev_node_info node_info; * */ -#ifdef CONFIG_HOTPLUG - -static int unloading = 0; static struct completion unload_sem; /* @@ -158,7 +154,7 @@ static int pnp_dock_thread(void *unused) int docked = -1, d = 0; set_freezable(); - while (!unloading) { + while (1) { int status; /* @@ -201,10 +197,7 @@ static int pnp_dock_thread(void *unused) complete_and_exit(&unload_sem, 0); } -#endif /* CONFIG_HOTPLUG */ - -static int pnpbios_get_resources(struct pnp_dev *dev, - struct pnp_resource_table *res) +static int pnpbios_get_resources(struct pnp_dev *dev) { u8 nodenum = dev->number; struct pnp_bios_node *node; @@ -212,6 +205,7 @@ static int pnpbios_get_resources(struct pnp_dev *dev, if (!pnpbios_is_dynamic(dev)) return -EPERM; + pnp_dbg(&dev->dev, "get resources\n"); node = kzalloc(node_info.max_node_size, GFP_KERNEL); if (!node) return -1; @@ -219,14 +213,13 @@ static int pnpbios_get_resources(struct pnp_dev *dev, kfree(node); return -ENODEV; } - pnpbios_read_resources_from_node(res, node); + pnpbios_read_resources_from_node(dev, node); dev->active = pnp_is_active(dev); kfree(node); return 0; } -static int pnpbios_set_resources(struct pnp_dev *dev, - struct pnp_resource_table *res) +static int pnpbios_set_resources(struct pnp_dev *dev) { u8 nodenum = dev->number; struct pnp_bios_node *node; @@ -235,6 +228,7 @@ static int pnpbios_set_resources(struct pnp_dev *dev, if (!pnpbios_is_dynamic(dev)) return -EPERM; + pnp_dbg(&dev->dev, "set resources\n"); node = kzalloc(node_info.max_node_size, GFP_KERNEL); if (!node) return -1; @@ -242,7 +236,7 @@ static int pnpbios_set_resources(struct pnp_dev *dev, kfree(node); return -ENODEV; } - if (pnpbios_write_resources_to_node(res, node) < 0) { + if (pnpbios_write_resources_to_node(dev, node) < 0) { kfree(node); return -1; } @@ -317,30 +311,21 @@ static int __init insert_device(struct pnp_bios_node *node) { struct list_head *pos; struct pnp_dev *dev; - struct pnp_id *dev_id; char id[8]; + int error; /* check if the device is already added */ list_for_each(pos, &pnpbios_protocol.devices) { dev = list_entry(pos, struct pnp_dev, protocol_list); if (dev->number == node->handle) - return -1; + return -EEXIST; } - dev = kzalloc(sizeof(struct pnp_dev), GFP_KERNEL); + pnp_eisa_id_to_string(node->eisa_id & PNP_EISA_ID_MASK, id); + dev = pnp_alloc_dev(&pnpbios_protocol, node->handle, id); if (!dev) - return -1; - - dev_id = kzalloc(sizeof(struct pnp_id), GFP_KERNEL); - if (!dev_id) { - kfree(dev); - return -1; - } + return -ENOMEM; - dev->number = node->handle; - pnpid32_to_pnpid(node->eisa_id, id); - memcpy(dev_id->id, id, 7); - pnp_add_id(dev_id, dev); pnpbios_parse_data_stream(dev, node); dev->active = pnp_is_active(dev); dev->flags = node->flags; @@ -353,13 +338,17 @@ static int __init insert_device(struct pnp_bios_node *node) dev->capabilities |= PNP_WRITE; if (dev->flags & PNPBIOS_REMOVABLE) dev->capabilities |= PNP_REMOVABLE; - dev->protocol = &pnpbios_protocol; /* clear out the damaged flags */ if (!dev->active) - pnp_init_resource_table(&dev->res); + pnp_init_resources(dev); + + error = pnp_add_device(dev); + if (error) { + put_device(&dev->dev); + return error; + } - pnp_add_device(dev); pnpbios_interface_attach_device(node); return 0; @@ -530,10 +519,6 @@ static int __init pnpbios_init(void) { int ret; -#if defined(CONFIG_PPC_MERGE) - if (check_legacy_ioport(PNPBIOS_BASE)) - return -ENODEV; -#endif if (pnpbios_disabled || dmi_check_system(pnpbios_dmi_table) || paravirt_enabled()) { printk(KERN_INFO "PnPBIOS: Disabled\n"); @@ -582,24 +567,20 @@ static int __init pnpbios_init(void) return 0; } -subsys_initcall(pnpbios_init); +fs_initcall(pnpbios_init); static int __init pnpbios_thread_init(void) { struct task_struct *task; -#if defined(CONFIG_PPC_MERGE) - if (check_legacy_ioport(PNPBIOS_BASE)) - return 0; -#endif if (pnpbios_disabled) return 0; -#ifdef CONFIG_HOTPLUG + init_completion(&unload_sem); task = kthread_run(pnp_dock_thread, NULL, "kpnpbiosd"); - if (!IS_ERR(task)) - unloading = 0; -#endif + if (IS_ERR(task)) + return PTR_ERR(task); + return 0; } diff --git a/drivers/pnp/pnpbios/pnpbios.h b/drivers/pnp/pnpbios/pnpbios.h index d8cb2fd1f12..b09cf6dc207 100644 --- a/drivers/pnp/pnpbios/pnpbios.h +++ b/drivers/pnp/pnpbios/pnpbios.h @@ -2,6 +2,142 @@ * pnpbios.h - contains local definitions */ +/* + * Include file for the interface to a PnP BIOS + * + * Original BIOS code (C) 1998 Christian Schmidt (chr.schmidt@tu-bs.de) + * PnP handler parts (c) 1998 Tom Lees <tom@lpsg.demon.co.uk> + * Minor reorganizations by David Hinds <dahinds@users.sourceforge.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Return codes + */ +#define PNP_SUCCESS 0x00 +#define PNP_NOT_SET_STATICALLY 0x7f +#define PNP_UNKNOWN_FUNCTION 0x81 +#define PNP_FUNCTION_NOT_SUPPORTED 0x82 +#define PNP_INVALID_HANDLE 0x83 +#define PNP_BAD_PARAMETER 0x84 +#define PNP_SET_FAILED 0x85 +#define PNP_EVENTS_NOT_PENDING 0x86 +#define PNP_SYSTEM_NOT_DOCKED 0x87 +#define PNP_NO_ISA_PNP_CARDS 0x88 +#define PNP_UNABLE_TO_DETERMINE_DOCK_CAPABILITIES 0x89 +#define PNP_CONFIG_CHANGE_FAILED_NO_BATTERY 0x8a +#define PNP_CONFIG_CHANGE_FAILED_RESOURCE_CONFLICT 0x8b +#define PNP_BUFFER_TOO_SMALL 0x8c +#define PNP_USE_ESCD_SUPPORT 0x8d +#define PNP_MESSAGE_NOT_SUPPORTED 0x8e +#define PNP_HARDWARE_ERROR 0x8f + +#define ESCD_SUCCESS 0x00 +#define ESCD_IO_ERROR_READING 0x55 +#define ESCD_INVALID 0x56 +#define ESCD_BUFFER_TOO_SMALL 0x59 +#define ESCD_NVRAM_TOO_SMALL 0x5a +#define ESCD_FUNCTION_NOT_SUPPORTED 0x81 + +/* + * Events that can be received by "get event" + */ +#define PNPEV_ABOUT_TO_CHANGE_CONFIG 0x0001 +#define PNPEV_DOCK_CHANGED 0x0002 +#define PNPEV_SYSTEM_DEVICE_CHANGED 0x0003 +#define PNPEV_CONFIG_CHANGED_FAILED 0x0004 +#define PNPEV_UNKNOWN_SYSTEM_EVENT 0xffff +/* 0x8000 through 0xfffe are OEM defined */ + +/* + * Messages that should be sent through "send message" + */ +#define PNPMSG_OK 0x00 +#define PNPMSG_ABORT 0x01 +#define PNPMSG_UNDOCK_DEFAULT_ACTION 0x40 +#define PNPMSG_POWER_OFF 0x41 +#define PNPMSG_PNP_OS_ACTIVE 0x42 +#define PNPMSG_PNP_OS_INACTIVE 0x43 + +/* + * Plug and Play BIOS flags + */ +#define PNPBIOS_NO_DISABLE 0x0001 +#define PNPBIOS_NO_CONFIG 0x0002 +#define PNPBIOS_OUTPUT 0x0004 +#define PNPBIOS_INPUT 0x0008 +#define PNPBIOS_BOOTABLE 0x0010 +#define PNPBIOS_DOCK 0x0020 +#define PNPBIOS_REMOVABLE 0x0040 +#define pnpbios_is_static(x) (((x)->flags & 0x0100) == 0x0000) +#define pnpbios_is_dynamic(x) ((x)->flags & 0x0080) + +/* + * Function Parameters + */ +#define PNPMODE_STATIC 1 +#define PNPMODE_DYNAMIC 0 + +/* 0x8000 through 0xffff are OEM defined */ + +#pragma pack(1) +struct pnp_dev_node_info { + __u16 no_nodes; + __u16 max_node_size; +}; +struct pnp_docking_station_info { + __u32 location_id; + __u32 serial; + __u16 capabilities; +}; +struct pnp_isa_config_struc { + __u8 revision; + __u8 no_csns; + __u16 isa_rd_data_port; + __u16 reserved; +}; +struct escd_info_struc { + __u16 min_escd_write_size; + __u16 escd_size; + __u32 nv_storage_base; +}; +struct pnp_bios_node { + __u16 size; + __u8 handle; + __u32 eisa_id; + __u8 type_code[3]; + __u16 flags; + __u8 data[0]; +}; +#pragma pack() + +/* non-exported */ +extern struct pnp_dev_node_info node_info; + +extern int pnp_bios_dev_node_info(struct pnp_dev_node_info *data); +extern int pnp_bios_get_dev_node(u8 *nodenum, char config, + struct pnp_bios_node *data); +extern int pnp_bios_set_dev_node(u8 nodenum, char config, + struct pnp_bios_node *data); +extern int pnp_bios_get_stat_res(char *info); +extern int pnp_bios_isapnp_config(struct pnp_isa_config_struc *data); +extern int pnp_bios_escd_info(struct escd_info_struc *data); +extern int pnp_bios_read_escd(char *data, u32 nvram_base); +extern int pnp_bios_dock_station_info(struct pnp_docking_station_info *data); + #pragma pack(1) union pnp_bios_install_struct { struct { @@ -28,8 +164,8 @@ extern int pnp_bios_present(void); extern int pnpbios_dont_use_current_config; extern int pnpbios_parse_data_stream(struct pnp_dev *dev, struct pnp_bios_node * node); -extern int pnpbios_read_resources_from_node(struct pnp_resource_table *res, struct pnp_bios_node * node); -extern int pnpbios_write_resources_to_node(struct pnp_resource_table *res, struct pnp_bios_node * node); +extern int pnpbios_read_resources_from_node(struct pnp_dev *dev, struct pnp_bios_node *node); +extern int pnpbios_write_resources_to_node(struct pnp_dev *dev, struct pnp_bios_node *node); extern void pnpid32_to_pnpid(u32 id, char *str); extern void pnpbios_print_status(const char * module, u16 status); diff --git a/drivers/pnp/pnpbios/proc.c b/drivers/pnp/pnpbios/proc.c index bb19bc957ba..c212db0fc65 100644 --- a/drivers/pnp/pnpbios/proc.c +++ b/drivers/pnp/pnpbios/proc.c @@ -11,7 +11,6 @@ * * The .../escd file is utilized by the lsescd utility written by * Gunther Mayer. - * http://home.t-online.de/home/gunther.mayer/lsescd * * The .../legacy_device_resources file is not used yet. * @@ -23,7 +22,8 @@ #include <linux/slab.h> #include <linux/types.h> #include <linux/proc_fs.h> -#include <linux/pnpbios.h> +#include <linux/pnp.h> +#include <linux/seq_file.h> #include <linux/init.h> #include <asm/uaccess.h> @@ -33,42 +33,65 @@ static struct proc_dir_entry *proc_pnp = NULL; static struct proc_dir_entry *proc_pnp_boot = NULL; -static int proc_read_pnpconfig(char *buf, char **start, off_t pos, - int count, int *eof, void *data) +static int pnpconfig_proc_show(struct seq_file *m, void *v) { struct pnp_isa_config_struc pnps; if (pnp_bios_isapnp_config(&pnps)) return -EIO; - return snprintf(buf, count, - "structure_revision %d\n" - "number_of_CSNs %d\n" - "ISA_read_data_port 0x%x\n", - pnps.revision, pnps.no_csns, pnps.isa_rd_data_port); + seq_printf(m, "structure_revision %d\n" + "number_of_CSNs %d\n" + "ISA_read_data_port 0x%x\n", + pnps.revision, pnps.no_csns, pnps.isa_rd_data_port); + return 0; } -static int proc_read_escdinfo(char *buf, char **start, off_t pos, - int count, int *eof, void *data) +static int pnpconfig_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, pnpconfig_proc_show, NULL); +} + +static const struct file_operations pnpconfig_proc_fops = { + .owner = THIS_MODULE, + .open = pnpconfig_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int escd_info_proc_show(struct seq_file *m, void *v) { struct escd_info_struc escd; if (pnp_bios_escd_info(&escd)) return -EIO; - return snprintf(buf, count, - "min_ESCD_write_size %d\n" + seq_printf(m, "min_ESCD_write_size %d\n" "ESCD_size %d\n" "NVRAM_base 0x%x\n", escd.min_escd_write_size, escd.escd_size, escd.nv_storage_base); + return 0; } +static int escd_info_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, escd_info_proc_show, NULL); +} + +static const struct file_operations escd_info_proc_fops = { + .owner = THIS_MODULE, + .open = escd_info_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + #define MAX_SANE_ESCD_SIZE (32*1024) -static int proc_read_escd(char *buf, char **start, off_t pos, - int count, int *eof, void *data) +static int escd_proc_show(struct seq_file *m, void *v) { struct escd_info_struc escd; char *tmpbuf; - int escd_size, escd_left_to_read, n; + int escd_size; if (pnp_bios_escd_info(&escd)) return -EIO; @@ -76,7 +99,7 @@ static int proc_read_escd(char *buf, char **start, off_t pos, /* sanity check */ if (escd.escd_size > MAX_SANE_ESCD_SIZE) { printk(KERN_ERR - "PnPBIOS: proc_read_escd: ESCD size reported by BIOS escd_info call is too great\n"); + "PnPBIOS: %s: ESCD size reported by BIOS escd_info call is too great\n", __func__); return -EFBIG; } @@ -94,79 +117,106 @@ static int proc_read_escd(char *buf, char **start, off_t pos, /* sanity check */ if (escd_size > MAX_SANE_ESCD_SIZE) { - printk(KERN_ERR "PnPBIOS: proc_read_escd: ESCD size reported by" - " BIOS read_escd call is too great\n"); + printk(KERN_ERR "PnPBIOS: %s: ESCD size reported by" + " BIOS read_escd call is too great\n", __func__); kfree(tmpbuf); return -EFBIG; } - escd_left_to_read = escd_size - pos; - if (escd_left_to_read < 0) - escd_left_to_read = 0; - if (escd_left_to_read == 0) - *eof = 1; - n = min(count, escd_left_to_read); - memcpy(buf, tmpbuf + pos, n); + seq_write(m, tmpbuf, escd_size); kfree(tmpbuf); - *start = buf; - return n; + return 0; } -static int proc_read_legacyres(char *buf, char **start, off_t pos, - int count, int *eof, void *data) +static int escd_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, escd_proc_show, NULL); +} + +static const struct file_operations escd_proc_fops = { + .owner = THIS_MODULE, + .open = escd_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int pnp_legacyres_proc_show(struct seq_file *m, void *v) { - /* Assume that the following won't overflow the buffer */ - if (pnp_bios_get_stat_res(buf)) + void *buf; + + buf = kmalloc(65536, GFP_KERNEL); + if (!buf) + return -ENOMEM; + if (pnp_bios_get_stat_res(buf)) { + kfree(buf); return -EIO; + } + + seq_write(m, buf, 65536); + kfree(buf); + return 0; +} - return count; // FIXME: Return actual length +static int pnp_legacyres_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, pnp_legacyres_proc_show, NULL); } -static int proc_read_devices(char *buf, char **start, off_t pos, - int count, int *eof, void *data) +static const struct file_operations pnp_legacyres_proc_fops = { + .owner = THIS_MODULE, + .open = pnp_legacyres_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int pnp_devices_proc_show(struct seq_file *m, void *v) { struct pnp_bios_node *node; u8 nodenum; - char *p = buf; - - if (pos >= 0xff) - return 0; node = kzalloc(node_info.max_node_size, GFP_KERNEL); if (!node) return -ENOMEM; - for (nodenum = pos; nodenum < 0xff;) { + for (nodenum = 0; nodenum < 0xff;) { u8 thisnodenum = nodenum; - /* 26 = the number of characters per line sprintf'ed */ - if ((p - buf + 26) > count) - break; + if (pnp_bios_get_dev_node(&nodenum, PNPMODE_DYNAMIC, node)) break; - p += sprintf(p, "%02x\t%08x\t%02x:%02x:%02x\t%04x\n", + seq_printf(m, "%02x\t%08x\t%3phC\t%04x\n", node->handle, node->eisa_id, - node->type_code[0], node->type_code[1], - node->type_code[2], node->flags); + node->type_code, node->flags); if (nodenum <= thisnodenum) { printk(KERN_ERR "%s Node number 0x%x is out of sequence following node 0x%x. Aborting.\n", "PnPBIOS: proc_read_devices:", (unsigned int)nodenum, (unsigned int)thisnodenum); - *eof = 1; break; } } kfree(node); - if (nodenum == 0xff) - *eof = 1; - *start = (char *)((off_t) nodenum - pos); - return p - buf; + return 0; +} + +static int pnp_devices_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, pnp_devices_proc_show, NULL); } -static int proc_read_node(char *buf, char **start, off_t pos, - int count, int *eof, void *data) +static const struct file_operations pnp_devices_proc_fops = { + .owner = THIS_MODULE, + .open = pnp_devices_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int pnpbios_proc_show(struct seq_file *m, void *v) { + void *data = m->private; struct pnp_bios_node *node; int boot = (long)data >> 8; u8 nodenum = (long)data; @@ -180,14 +230,20 @@ static int proc_read_node(char *buf, char **start, off_t pos, return -EIO; } len = node->size - sizeof(struct pnp_bios_node); - memcpy(buf, node->data, len); + seq_write(m, node->data, len); kfree(node); - return len; + return 0; +} + +static int pnpbios_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, pnpbios_proc_show, PDE_DATA(inode)); } -static int proc_write_node(struct file *file, const char __user * buf, - unsigned long count, void *data) +static ssize_t pnpbios_proc_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) { + void *data = PDE_DATA(file_inode(file)); struct pnp_bios_node *node; int boot = (long)data >> 8; u8 nodenum = (long)data; @@ -218,34 +274,33 @@ out: return ret; } +static const struct file_operations pnpbios_proc_fops = { + .owner = THIS_MODULE, + .open = pnpbios_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = pnpbios_proc_write, +}; + int pnpbios_interface_attach_device(struct pnp_bios_node *node) { char name[3]; - struct proc_dir_entry *ent; sprintf(name, "%02x", node->handle); if (!proc_pnp) return -EIO; if (!pnpbios_dont_use_current_config) { - ent = create_proc_entry(name, 0, proc_pnp); - if (ent) { - ent->read_proc = proc_read_node; - ent->write_proc = proc_write_node; - ent->data = (void *)(long)(node->handle); - } + proc_create_data(name, 0644, proc_pnp, &pnpbios_proc_fops, + (void *)(long)(node->handle)); } if (!proc_pnp_boot) return -EIO; - ent = create_proc_entry(name, 0, proc_pnp_boot); - if (ent) { - ent->read_proc = proc_read_node; - ent->write_proc = proc_write_node; - ent->data = (void *)(long)(node->handle + 0x100); + if (proc_create_data(name, 0644, proc_pnp_boot, &pnpbios_proc_fops, + (void *)(long)(node->handle + 0x100))) return 0; - } - return -EIO; } @@ -256,20 +311,17 @@ int pnpbios_interface_attach_device(struct pnp_bios_node *node) */ int __init pnpbios_proc_init(void) { - proc_pnp = proc_mkdir("pnp", proc_bus); + proc_pnp = proc_mkdir("bus/pnp", NULL); if (!proc_pnp) return -EIO; proc_pnp_boot = proc_mkdir("boot", proc_pnp); if (!proc_pnp_boot) return -EIO; - create_proc_read_entry("devices", 0, proc_pnp, proc_read_devices, NULL); - create_proc_read_entry("configuration_info", 0, proc_pnp, - proc_read_pnpconfig, NULL); - create_proc_read_entry("escd_info", 0, proc_pnp, proc_read_escdinfo, - NULL); - create_proc_read_entry("escd", S_IRUSR, proc_pnp, proc_read_escd, NULL); - create_proc_read_entry("legacy_device_resources", 0, proc_pnp, - proc_read_legacyres, NULL); + proc_create("devices", 0, proc_pnp, &pnp_devices_proc_fops); + proc_create("configuration_info", 0, proc_pnp, &pnpconfig_proc_fops); + proc_create("escd_info", 0, proc_pnp, &escd_info_proc_fops); + proc_create("escd", S_IRUSR, proc_pnp, &escd_proc_fops); + proc_create("legacy_device_resources", 0, proc_pnp, &pnp_legacyres_proc_fops); return 0; } @@ -294,5 +346,5 @@ void __exit pnpbios_proc_exit(void) remove_proc_entry("configuration_info", proc_pnp); remove_proc_entry("devices", proc_pnp); remove_proc_entry("boot", proc_pnp); - remove_proc_entry("pnp", proc_bus); + remove_proc_entry("bus/pnp", NULL); } diff --git a/drivers/pnp/pnpbios/rsparser.c b/drivers/pnp/pnpbios/rsparser.c index caade353141..cca2f9f9f3e 100644 --- a/drivers/pnp/pnpbios/rsparser.c +++ b/drivers/pnp/pnpbios/rsparser.c @@ -4,9 +4,7 @@ #include <linux/ctype.h> #include <linux/pnp.h> -#include <linux/pnpbios.h> #include <linux/string.h> -#include <linux/slab.h> #ifdef CONFIG_PCI #include <linux/pci.h> @@ -16,6 +14,7 @@ inline void pcibios_penalize_isa_irq(int irq, int active) } #endif /* CONFIG_PCI */ +#include "../base.h" #include "pnpbios.h" /* standard resource tags */ @@ -53,97 +52,43 @@ inline void pcibios_penalize_isa_irq(int irq, int active) * Allocated Resources */ -static void pnpbios_parse_allocated_irqresource(struct pnp_resource_table *res, - int irq) +static void pnpbios_parse_allocated_ioresource(struct pnp_dev *dev, + int start, int len) { - int i = 0; - - while (!(res->irq_resource[i].flags & IORESOURCE_UNSET) - && i < PNP_MAX_IRQ) - i++; - if (i < PNP_MAX_IRQ) { - res->irq_resource[i].flags = IORESOURCE_IRQ; // Also clears _UNSET flag - if (irq == -1) { - res->irq_resource[i].flags |= IORESOURCE_DISABLED; - return; - } - res->irq_resource[i].start = - res->irq_resource[i].end = (unsigned long)irq; - pcibios_penalize_isa_irq(irq, 1); - } -} + int flags = 0; + int end = start + len - 1; -static void pnpbios_parse_allocated_dmaresource(struct pnp_resource_table *res, - int dma) -{ - int i = 0; - - while (i < PNP_MAX_DMA && - !(res->dma_resource[i].flags & IORESOURCE_UNSET)) - i++; - if (i < PNP_MAX_DMA) { - res->dma_resource[i].flags = IORESOURCE_DMA; // Also clears _UNSET flag - if (dma == -1) { - res->dma_resource[i].flags |= IORESOURCE_DISABLED; - return; - } - res->dma_resource[i].start = - res->dma_resource[i].end = (unsigned long)dma; - } -} + if (len <= 0 || end >= 0x10003) + flags |= IORESOURCE_DISABLED; -static void pnpbios_parse_allocated_ioresource(struct pnp_resource_table *res, - int io, int len) -{ - int i = 0; - - while (!(res->port_resource[i].flags & IORESOURCE_UNSET) - && i < PNP_MAX_PORT) - i++; - if (i < PNP_MAX_PORT) { - res->port_resource[i].flags = IORESOURCE_IO; // Also clears _UNSET flag - if (len <= 0 || (io + len - 1) >= 0x10003) { - res->port_resource[i].flags |= IORESOURCE_DISABLED; - return; - } - res->port_resource[i].start = (unsigned long)io; - res->port_resource[i].end = (unsigned long)(io + len - 1); - } + pnp_add_io_resource(dev, start, end, flags); } -static void pnpbios_parse_allocated_memresource(struct pnp_resource_table *res, - int mem, int len) +static void pnpbios_parse_allocated_memresource(struct pnp_dev *dev, + int start, int len) { - int i = 0; - - while (!(res->mem_resource[i].flags & IORESOURCE_UNSET) - && i < PNP_MAX_MEM) - i++; - if (i < PNP_MAX_MEM) { - res->mem_resource[i].flags = IORESOURCE_MEM; // Also clears _UNSET flag - if (len <= 0) { - res->mem_resource[i].flags |= IORESOURCE_DISABLED; - return; - } - res->mem_resource[i].start = (unsigned long)mem; - res->mem_resource[i].end = (unsigned long)(mem + len - 1); - } + int flags = 0; + int end = start + len - 1; + + if (len <= 0) + flags |= IORESOURCE_DISABLED; + + pnp_add_mem_resource(dev, start, end, flags); } -static unsigned char *pnpbios_parse_allocated_resource_data(unsigned char *p, - unsigned char *end, - struct - pnp_resource_table - *res) +static unsigned char *pnpbios_parse_allocated_resource_data(struct pnp_dev *dev, + unsigned char *p, + unsigned char *end) { unsigned int len, tag; - int io, size, mask, i; + int io, size, mask, i, flags; if (!p) return NULL; - /* Blank the resource table values */ - pnp_init_resource_table(res); + pnp_dbg(&dev->dev, "parse allocated resources\n"); + + pnp_init_resources(dev); while ((char *)p < (char *)end) { @@ -163,7 +108,7 @@ static unsigned char *pnpbios_parse_allocated_resource_data(unsigned char *p, goto len_err; io = *(short *)&p[4]; size = *(short *)&p[10]; - pnpbios_parse_allocated_memresource(res, io, size); + pnpbios_parse_allocated_memresource(dev, io, size); break; case LARGE_TAG_ANSISTR: @@ -179,7 +124,7 @@ static unsigned char *pnpbios_parse_allocated_resource_data(unsigned char *p, goto len_err; io = *(int *)&p[4]; size = *(int *)&p[16]; - pnpbios_parse_allocated_memresource(res, io, size); + pnpbios_parse_allocated_memresource(dev, io, size); break; case LARGE_TAG_FIXEDMEM32: @@ -187,29 +132,37 @@ static unsigned char *pnpbios_parse_allocated_resource_data(unsigned char *p, goto len_err; io = *(int *)&p[4]; size = *(int *)&p[8]; - pnpbios_parse_allocated_memresource(res, io, size); + pnpbios_parse_allocated_memresource(dev, io, size); break; case SMALL_TAG_IRQ: if (len < 2 || len > 3) goto len_err; + flags = 0; io = -1; mask = p[1] + p[2] * 256; for (i = 0; i < 16; i++, mask = mask >> 1) if (mask & 0x01) io = i; - pnpbios_parse_allocated_irqresource(res, io); + if (io != -1) + pcibios_penalize_isa_irq(io, 1); + else + flags = IORESOURCE_DISABLED; + pnp_add_irq_resource(dev, io, flags); break; case SMALL_TAG_DMA: if (len != 2) goto len_err; + flags = 0; io = -1; mask = p[1]; for (i = 0; i < 8; i++, mask = mask >> 1) if (mask & 0x01) io = i; - pnpbios_parse_allocated_dmaresource(res, io); + if (io == -1) + flags = IORESOURCE_DISABLED; + pnp_add_dma_resource(dev, io, flags); break; case SMALL_TAG_PORT: @@ -217,7 +170,7 @@ static unsigned char *pnpbios_parse_allocated_resource_data(unsigned char *p, goto len_err; io = p[2] + p[3] * 256; size = p[7]; - pnpbios_parse_allocated_ioresource(res, io, size); + pnpbios_parse_allocated_ioresource(dev, io, size); break; case SMALL_TAG_VENDOR: @@ -229,7 +182,7 @@ static unsigned char *pnpbios_parse_allocated_resource_data(unsigned char *p, goto len_err; io = p[1] + p[2] * 256; size = p[3]; - pnpbios_parse_allocated_ioresource(res, io, size); + pnpbios_parse_allocated_ioresource(dev, io, size); break; case SMALL_TAG_END: @@ -237,11 +190,10 @@ static unsigned char *pnpbios_parse_allocated_resource_data(unsigned char *p, return (unsigned char *)p; break; - default: /* an unkown tag */ + default: /* an unknown tag */ len_err: - printk(KERN_ERR - "PnPBIOS: Unknown tag '0x%x', length '%d'.\n", - tag, len); + dev_err(&dev->dev, "unknown tag %#x length %d\n", + tag, len); break; } @@ -252,8 +204,7 @@ len_err: p += len + 1; } - printk(KERN_ERR - "PnPBIOS: Resource structure does not contain an end tag.\n"); + dev_err(&dev->dev, "no end tag in resource structure\n"); return NULL; } @@ -262,130 +213,118 @@ len_err: * Resource Configuration Options */ -static __init void pnpbios_parse_mem_option(unsigned char *p, int size, - struct pnp_option *option) +static __init void pnpbios_parse_mem_option(struct pnp_dev *dev, + unsigned char *p, int size, + unsigned int option_flags) { - struct pnp_mem *mem; - - mem = kzalloc(sizeof(struct pnp_mem), GFP_KERNEL); - if (!mem) - return; - mem->min = ((p[5] << 8) | p[4]) << 8; - mem->max = ((p[7] << 8) | p[6]) << 8; - mem->align = (p[9] << 8) | p[8]; - mem->size = ((p[11] << 8) | p[10]) << 8; - mem->flags = p[3]; - pnp_register_mem_resource(option, mem); + resource_size_t min, max, align, len; + unsigned char flags; + + min = ((p[5] << 8) | p[4]) << 8; + max = ((p[7] << 8) | p[6]) << 8; + align = (p[9] << 8) | p[8]; + len = ((p[11] << 8) | p[10]) << 8; + flags = p[3]; + pnp_register_mem_resource(dev, option_flags, min, max, align, len, + flags); } -static __init void pnpbios_parse_mem32_option(unsigned char *p, int size, - struct pnp_option *option) +static __init void pnpbios_parse_mem32_option(struct pnp_dev *dev, + unsigned char *p, int size, + unsigned int option_flags) { - struct pnp_mem *mem; - - mem = kzalloc(sizeof(struct pnp_mem), GFP_KERNEL); - if (!mem) - return; - mem->min = (p[7] << 24) | (p[6] << 16) | (p[5] << 8) | p[4]; - mem->max = (p[11] << 24) | (p[10] << 16) | (p[9] << 8) | p[8]; - mem->align = (p[15] << 24) | (p[14] << 16) | (p[13] << 8) | p[12]; - mem->size = (p[19] << 24) | (p[18] << 16) | (p[17] << 8) | p[16]; - mem->flags = p[3]; - pnp_register_mem_resource(option, mem); + resource_size_t min, max, align, len; + unsigned char flags; + + min = (p[7] << 24) | (p[6] << 16) | (p[5] << 8) | p[4]; + max = (p[11] << 24) | (p[10] << 16) | (p[9] << 8) | p[8]; + align = (p[15] << 24) | (p[14] << 16) | (p[13] << 8) | p[12]; + len = (p[19] << 24) | (p[18] << 16) | (p[17] << 8) | p[16]; + flags = p[3]; + pnp_register_mem_resource(dev, option_flags, min, max, align, len, + flags); } -static __init void pnpbios_parse_fixed_mem32_option(unsigned char *p, int size, - struct pnp_option *option) +static __init void pnpbios_parse_fixed_mem32_option(struct pnp_dev *dev, + unsigned char *p, int size, + unsigned int option_flags) { - struct pnp_mem *mem; - - mem = kzalloc(sizeof(struct pnp_mem), GFP_KERNEL); - if (!mem) - return; - mem->min = mem->max = (p[7] << 24) | (p[6] << 16) | (p[5] << 8) | p[4]; - mem->size = (p[11] << 24) | (p[10] << 16) | (p[9] << 8) | p[8]; - mem->align = 0; - mem->flags = p[3]; - pnp_register_mem_resource(option, mem); + resource_size_t base, len; + unsigned char flags; + + base = (p[7] << 24) | (p[6] << 16) | (p[5] << 8) | p[4]; + len = (p[11] << 24) | (p[10] << 16) | (p[9] << 8) | p[8]; + flags = p[3]; + pnp_register_mem_resource(dev, option_flags, base, base, 0, len, flags); } -static __init void pnpbios_parse_irq_option(unsigned char *p, int size, - struct pnp_option *option) +static __init void pnpbios_parse_irq_option(struct pnp_dev *dev, + unsigned char *p, int size, + unsigned int option_flags) { - struct pnp_irq *irq; unsigned long bits; + pnp_irq_mask_t map; + unsigned char flags = IORESOURCE_IRQ_HIGHEDGE; - irq = kzalloc(sizeof(struct pnp_irq), GFP_KERNEL); - if (!irq) - return; bits = (p[2] << 8) | p[1]; - bitmap_copy(irq->map, &bits, 16); + + bitmap_zero(map.bits, PNP_IRQ_NR); + bitmap_copy(map.bits, &bits, 16); + if (size > 2) - irq->flags = p[3]; - else - irq->flags = IORESOURCE_IRQ_HIGHEDGE; - pnp_register_irq_resource(option, irq); + flags = p[3]; + + pnp_register_irq_resource(dev, option_flags, &map, flags); } -static __init void pnpbios_parse_dma_option(unsigned char *p, int size, - struct pnp_option *option) +static __init void pnpbios_parse_dma_option(struct pnp_dev *dev, + unsigned char *p, int size, + unsigned int option_flags) { - struct pnp_dma *dma; - - dma = kzalloc(sizeof(struct pnp_dma), GFP_KERNEL); - if (!dma) - return; - dma->map = p[1]; - dma->flags = p[2]; - pnp_register_dma_resource(option, dma); + pnp_register_dma_resource(dev, option_flags, p[1], p[2]); } -static __init void pnpbios_parse_port_option(unsigned char *p, int size, - struct pnp_option *option) +static __init void pnpbios_parse_port_option(struct pnp_dev *dev, + unsigned char *p, int size, + unsigned int option_flags) { - struct pnp_port *port; - - port = kzalloc(sizeof(struct pnp_port), GFP_KERNEL); - if (!port) - return; - port->min = (p[3] << 8) | p[2]; - port->max = (p[5] << 8) | p[4]; - port->align = p[6]; - port->size = p[7]; - port->flags = p[1] ? PNP_PORT_FLAG_16BITADDR : 0; - pnp_register_port_resource(option, port); + resource_size_t min, max, align, len; + unsigned char flags; + + min = (p[3] << 8) | p[2]; + max = (p[5] << 8) | p[4]; + align = p[6]; + len = p[7]; + flags = p[1] ? IORESOURCE_IO_16BIT_ADDR : 0; + pnp_register_port_resource(dev, option_flags, min, max, align, len, + flags); } -static __init void pnpbios_parse_fixed_port_option(unsigned char *p, int size, - struct pnp_option *option) +static __init void pnpbios_parse_fixed_port_option(struct pnp_dev *dev, + unsigned char *p, int size, + unsigned int option_flags) { - struct pnp_port *port; - - port = kzalloc(sizeof(struct pnp_port), GFP_KERNEL); - if (!port) - return; - port->min = port->max = (p[2] << 8) | p[1]; - port->size = p[3]; - port->align = 0; - port->flags = PNP_PORT_FLAG_FIXED; - pnp_register_port_resource(option, port); + resource_size_t base, len; + + base = (p[2] << 8) | p[1]; + len = p[3]; + pnp_register_port_resource(dev, option_flags, base, base, 0, len, + IORESOURCE_IO_FIXED); } static __init unsigned char * pnpbios_parse_resource_option_data(unsigned char *p, unsigned char *end, - struct pnp_dev *dev) + struct pnp_dev *dev) { unsigned int len, tag; - int priority = 0; - struct pnp_option *option, *option_independent; + int priority; + unsigned int option_flags; if (!p) return NULL; - option_independent = option = pnp_register_independent_option(dev); - if (!option) - return NULL; - + pnp_dbg(&dev->dev, "parse resource options\n"); + option_flags = 0; while ((char *)p < (char *)end) { /* determine the type of tag */ @@ -402,37 +341,38 @@ pnpbios_parse_resource_option_data(unsigned char *p, unsigned char *end, case LARGE_TAG_MEM: if (len != 9) goto len_err; - pnpbios_parse_mem_option(p, len, option); + pnpbios_parse_mem_option(dev, p, len, option_flags); break; case LARGE_TAG_MEM32: if (len != 17) goto len_err; - pnpbios_parse_mem32_option(p, len, option); + pnpbios_parse_mem32_option(dev, p, len, option_flags); break; case LARGE_TAG_FIXEDMEM32: if (len != 9) goto len_err; - pnpbios_parse_fixed_mem32_option(p, len, option); + pnpbios_parse_fixed_mem32_option(dev, p, len, + option_flags); break; case SMALL_TAG_IRQ: if (len < 2 || len > 3) goto len_err; - pnpbios_parse_irq_option(p, len, option); + pnpbios_parse_irq_option(dev, p, len, option_flags); break; case SMALL_TAG_DMA: if (len != 2) goto len_err; - pnpbios_parse_dma_option(p, len, option); + pnpbios_parse_dma_option(dev, p, len, option_flags); break; case SMALL_TAG_PORT: if (len != 7) goto len_err; - pnpbios_parse_port_option(p, len, option); + pnpbios_parse_port_option(dev, p, len, option_flags); break; case SMALL_TAG_VENDOR: @@ -442,37 +382,32 @@ pnpbios_parse_resource_option_data(unsigned char *p, unsigned char *end, case SMALL_TAG_FIXEDPORT: if (len != 3) goto len_err; - pnpbios_parse_fixed_port_option(p, len, option); + pnpbios_parse_fixed_port_option(dev, p, len, + option_flags); break; case SMALL_TAG_STARTDEP: if (len > 1) goto len_err; - priority = 0x100 | PNP_RES_PRIORITY_ACCEPTABLE; + priority = PNP_RES_PRIORITY_ACCEPTABLE; if (len > 0) - priority = 0x100 | p[1]; - option = pnp_register_dependent_option(dev, priority); - if (!option) - return NULL; + priority = p[1]; + option_flags = pnp_new_dependent_set(dev, priority); break; case SMALL_TAG_ENDDEP: if (len != 0) goto len_err; - if (option_independent == option) - printk(KERN_WARNING - "PnPBIOS: Missing SMALL_TAG_STARTDEP tag\n"); - option = option_independent; + option_flags = 0; break; case SMALL_TAG_END: return p + 2; - default: /* an unkown tag */ + default: /* an unknown tag */ len_err: - printk(KERN_ERR - "PnPBIOS: Unknown tag '0x%x', length '%d'.\n", - tag, len); + dev_err(&dev->dev, "unknown tag %#x length %d\n", + tag, len); break; } @@ -483,8 +418,7 @@ len_err: p += len + 1; } - printk(KERN_ERR - "PnPBIOS: Resource structure does not contain an end tag.\n"); + dev_err(&dev->dev, "no end tag in resource structure\n"); return NULL; } @@ -493,32 +427,12 @@ len_err: * Compatible Device IDs */ -#define HEX(id,a) hex[((id)>>a) & 15] -#define CHAR(id,a) (0x40 + (((id)>>a) & 31)) - -void pnpid32_to_pnpid(u32 id, char *str) -{ - const char *hex = "0123456789abcdef"; - - id = be32_to_cpu(id); - str[0] = CHAR(id, 26); - str[1] = CHAR(id, 21); - str[2] = CHAR(id, 16); - str[3] = HEX(id, 12); - str[4] = HEX(id, 8); - str[5] = HEX(id, 4); - str[6] = HEX(id, 0); - str[7] = '\0'; -} - -#undef CHAR -#undef HEX - static unsigned char *pnpbios_parse_compatible_ids(unsigned char *p, unsigned char *end, struct pnp_dev *dev) { int len, tag; + u32 eisa_id; char id[8]; struct pnp_id *dev_id; @@ -548,13 +462,11 @@ static unsigned char *pnpbios_parse_compatible_ids(unsigned char *p, case SMALL_TAG_COMPATDEVID: /* compatible ID */ if (len != 4) goto len_err; - dev_id = kzalloc(sizeof(struct pnp_id), GFP_KERNEL); + eisa_id = p[1] | p[2] << 8 | p[3] << 16 | p[4] << 24; + pnp_eisa_id_to_string(eisa_id & PNP_EISA_ID_MASK, id); + dev_id = pnp_add_id(dev, id); if (!dev_id) return NULL; - pnpid32_to_pnpid(p[1] | p[2] << 8 | p[3] << 16 | p[4] << - 24, id); - memcpy(&dev_id->id, id, 7); - pnp_add_id(dev_id, dev); break; case SMALL_TAG_END: @@ -562,11 +474,10 @@ static unsigned char *pnpbios_parse_compatible_ids(unsigned char *p, return (unsigned char *)p; break; - default: /* an unkown tag */ + default: /* an unknown tag */ len_err: - printk(KERN_ERR - "PnPBIOS: Unknown tag '0x%x', length '%d'.\n", - tag, len); + dev_err(&dev->dev, "unknown tag %#x length %d\n", + tag, len); break; } @@ -577,8 +488,7 @@ len_err: p += len + 1; } - printk(KERN_ERR - "PnPBIOS: Resource structure does not contain an end tag.\n"); + dev_err(&dev->dev, "no end tag in resource structure\n"); return NULL; } @@ -587,10 +497,19 @@ len_err: * Allocated Resource Encoding */ -static void pnpbios_encode_mem(unsigned char *p, struct resource *res) +static void pnpbios_encode_mem(struct pnp_dev *dev, unsigned char *p, + struct resource *res) { - unsigned long base = res->start; - unsigned long len = res->end - res->start + 1; + unsigned long base; + unsigned long len; + + if (pnp_resource_enabled(res)) { + base = res->start; + len = resource_size(res); + } else { + base = 0; + len = 0; + } p[4] = (base >> 8) & 0xff; p[5] = ((base >> 8) >> 8) & 0xff; @@ -598,12 +517,23 @@ static void pnpbios_encode_mem(unsigned char *p, struct resource *res) p[7] = ((base >> 8) >> 8) & 0xff; p[10] = (len >> 8) & 0xff; p[11] = ((len >> 8) >> 8) & 0xff; + + pnp_dbg(&dev->dev, " encode mem %#lx-%#lx\n", base, base + len - 1); } -static void pnpbios_encode_mem32(unsigned char *p, struct resource *res) +static void pnpbios_encode_mem32(struct pnp_dev *dev, unsigned char *p, + struct resource *res) { - unsigned long base = res->start; - unsigned long len = res->end - res->start + 1; + unsigned long base; + unsigned long len; + + if (pnp_resource_enabled(res)) { + base = res->start; + len = resource_size(res); + } else { + base = 0; + len = 0; + } p[4] = base & 0xff; p[5] = (base >> 8) & 0xff; @@ -617,12 +547,23 @@ static void pnpbios_encode_mem32(unsigned char *p, struct resource *res) p[17] = (len >> 8) & 0xff; p[18] = (len >> 16) & 0xff; p[19] = (len >> 24) & 0xff; + + pnp_dbg(&dev->dev, " encode mem32 %#lx-%#lx\n", base, base + len - 1); } -static void pnpbios_encode_fixed_mem32(unsigned char *p, struct resource *res) +static void pnpbios_encode_fixed_mem32(struct pnp_dev *dev, unsigned char *p, + struct resource *res) { - unsigned long base = res->start; - unsigned long len = res->end - res->start + 1; + unsigned long base; + unsigned long len; + + if (pnp_resource_enabled(res)) { + base = res->start; + len = resource_size(res); + } else { + base = 0; + len = 0; + } p[4] = base & 0xff; p[5] = (base >> 8) & 0xff; @@ -632,52 +573,91 @@ static void pnpbios_encode_fixed_mem32(unsigned char *p, struct resource *res) p[9] = (len >> 8) & 0xff; p[10] = (len >> 16) & 0xff; p[11] = (len >> 24) & 0xff; + + pnp_dbg(&dev->dev, " encode fixed_mem32 %#lx-%#lx\n", base, + base + len - 1); } -static void pnpbios_encode_irq(unsigned char *p, struct resource *res) +static void pnpbios_encode_irq(struct pnp_dev *dev, unsigned char *p, + struct resource *res) { - unsigned long map = 0; + unsigned long map; + + if (pnp_resource_enabled(res)) + map = 1 << res->start; + else + map = 0; - map = 1 << res->start; p[1] = map & 0xff; p[2] = (map >> 8) & 0xff; + + pnp_dbg(&dev->dev, " encode irq mask %#lx\n", map); } -static void pnpbios_encode_dma(unsigned char *p, struct resource *res) +static void pnpbios_encode_dma(struct pnp_dev *dev, unsigned char *p, + struct resource *res) { - unsigned long map = 0; + unsigned long map; + + if (pnp_resource_enabled(res)) + map = 1 << res->start; + else + map = 0; - map = 1 << res->start; p[1] = map & 0xff; + + pnp_dbg(&dev->dev, " encode dma mask %#lx\n", map); } -static void pnpbios_encode_port(unsigned char *p, struct resource *res) +static void pnpbios_encode_port(struct pnp_dev *dev, unsigned char *p, + struct resource *res) { - unsigned long base = res->start; - unsigned long len = res->end - res->start + 1; + unsigned long base; + unsigned long len; + + if (pnp_resource_enabled(res)) { + base = res->start; + len = resource_size(res); + } else { + base = 0; + len = 0; + } p[2] = base & 0xff; p[3] = (base >> 8) & 0xff; p[4] = base & 0xff; p[5] = (base >> 8) & 0xff; p[7] = len & 0xff; + + pnp_dbg(&dev->dev, " encode io %#lx-%#lx\n", base, base + len - 1); } -static void pnpbios_encode_fixed_port(unsigned char *p, struct resource *res) +static void pnpbios_encode_fixed_port(struct pnp_dev *dev, unsigned char *p, + struct resource *res) { unsigned long base = res->start; - unsigned long len = res->end - res->start + 1; + unsigned long len = resource_size(res); + + if (pnp_resource_enabled(res)) { + base = res->start; + len = resource_size(res); + } else { + base = 0; + len = 0; + } p[1] = base & 0xff; p[2] = (base >> 8) & 0xff; p[3] = len & 0xff; + + pnp_dbg(&dev->dev, " encode fixed_io %#lx-%#lx\n", base, + base + len - 1); } -static unsigned char *pnpbios_encode_allocated_resource_data(unsigned char *p, - unsigned char *end, - struct - pnp_resource_table - *res) +static unsigned char *pnpbios_encode_allocated_resource_data(struct pnp_dev + *dev, + unsigned char *p, + unsigned char *end) { unsigned int len, tag; int port = 0, irq = 0, dma = 0, mem = 0; @@ -701,42 +681,48 @@ static unsigned char *pnpbios_encode_allocated_resource_data(unsigned char *p, case LARGE_TAG_MEM: if (len != 9) goto len_err; - pnpbios_encode_mem(p, &res->mem_resource[mem]); + pnpbios_encode_mem(dev, p, + pnp_get_resource(dev, IORESOURCE_MEM, mem)); mem++; break; case LARGE_TAG_MEM32: if (len != 17) goto len_err; - pnpbios_encode_mem32(p, &res->mem_resource[mem]); + pnpbios_encode_mem32(dev, p, + pnp_get_resource(dev, IORESOURCE_MEM, mem)); mem++; break; case LARGE_TAG_FIXEDMEM32: if (len != 9) goto len_err; - pnpbios_encode_fixed_mem32(p, &res->mem_resource[mem]); + pnpbios_encode_fixed_mem32(dev, p, + pnp_get_resource(dev, IORESOURCE_MEM, mem)); mem++; break; case SMALL_TAG_IRQ: if (len < 2 || len > 3) goto len_err; - pnpbios_encode_irq(p, &res->irq_resource[irq]); + pnpbios_encode_irq(dev, p, + pnp_get_resource(dev, IORESOURCE_IRQ, irq)); irq++; break; case SMALL_TAG_DMA: if (len != 2) goto len_err; - pnpbios_encode_dma(p, &res->dma_resource[dma]); + pnpbios_encode_dma(dev, p, + pnp_get_resource(dev, IORESOURCE_DMA, dma)); dma++; break; case SMALL_TAG_PORT: if (len != 7) goto len_err; - pnpbios_encode_port(p, &res->port_resource[port]); + pnpbios_encode_port(dev, p, + pnp_get_resource(dev, IORESOURCE_IO, port)); port++; break; @@ -747,7 +733,8 @@ static unsigned char *pnpbios_encode_allocated_resource_data(unsigned char *p, case SMALL_TAG_FIXEDPORT: if (len != 3) goto len_err; - pnpbios_encode_fixed_port(p, &res->port_resource[port]); + pnpbios_encode_fixed_port(dev, p, + pnp_get_resource(dev, IORESOURCE_IO, port)); port++; break; @@ -756,11 +743,10 @@ static unsigned char *pnpbios_encode_allocated_resource_data(unsigned char *p, return (unsigned char *)p; break; - default: /* an unkown tag */ + default: /* an unknown tag */ len_err: - printk(KERN_ERR - "PnPBIOS: Unknown tag '0x%x', length '%d'.\n", - tag, len); + dev_err(&dev->dev, "unknown tag %#x length %d\n", + tag, len); break; } @@ -771,8 +757,7 @@ len_err: p += len + 1; } - printk(KERN_ERR - "PnPBIOS: Resource structure does not contain an end tag.\n"); + dev_err(&dev->dev, "no end tag in resource structure\n"); return NULL; } @@ -787,7 +772,7 @@ int __init pnpbios_parse_data_stream(struct pnp_dev *dev, unsigned char *p = (char *)node->data; unsigned char *end = (char *)(node->data + node->size); - p = pnpbios_parse_allocated_resource_data(p, end, &dev->res); + p = pnpbios_parse_allocated_resource_data(dev, p, end); if (!p) return -EIO; p = pnpbios_parse_resource_option_data(p, end, dev); @@ -799,25 +784,25 @@ int __init pnpbios_parse_data_stream(struct pnp_dev *dev, return 0; } -int pnpbios_read_resources_from_node(struct pnp_resource_table *res, +int pnpbios_read_resources_from_node(struct pnp_dev *dev, struct pnp_bios_node *node) { unsigned char *p = (char *)node->data; unsigned char *end = (char *)(node->data + node->size); - p = pnpbios_parse_allocated_resource_data(p, end, res); + p = pnpbios_parse_allocated_resource_data(dev, p, end); if (!p) return -EIO; return 0; } -int pnpbios_write_resources_to_node(struct pnp_resource_table *res, +int pnpbios_write_resources_to_node(struct pnp_dev *dev, struct pnp_bios_node *node) { unsigned char *p = (char *)node->data; unsigned char *end = (char *)(node->data + node->size); - p = pnpbios_encode_allocated_resource_data(p, end, res); + p = pnpbios_encode_allocated_resource_data(dev, p, end); if (!p) return -EIO; return 0; diff --git a/drivers/pnp/quirks.c b/drivers/pnp/quirks.c index 37993206ae5..ebf0d6710b5 100644 --- a/drivers/pnp/quirks.c +++ b/drivers/pnp/quirks.c @@ -5,6 +5,8 @@ * when building up the resource structure for the first time. * * Copyright (c) 2000 Peter Denison <peterd@pnd-pc.demon.co.uk> + * Copyright (C) 2008 Hewlett-Packard Development Company, L.P. + * Bjorn Helgaas <bjorn.helgaas@hp.com> * * Heavily based on PCI quirks handling which is * @@ -13,6 +15,7 @@ #include <linux/types.h> #include <linux/kernel.h> +#include <linux/pci.h> #include <linux/string.h> #include <linux/slab.h> #include <linux/pnp.h> @@ -20,100 +23,215 @@ #include <linux/kallsyms.h> #include "base.h" +static void quirk_awe32_add_ports(struct pnp_dev *dev, + struct pnp_option *option, + unsigned int offset) +{ + struct pnp_option *new_option; + + new_option = kmalloc(sizeof(struct pnp_option), GFP_KERNEL); + if (!new_option) { + dev_err(&dev->dev, "couldn't add ioport region to option set " + "%d\n", pnp_option_set(option)); + return; + } + + *new_option = *option; + new_option->u.port.min += offset; + new_option->u.port.max += offset; + list_add(&new_option->list, &option->list); + + dev_info(&dev->dev, "added ioport region %#llx-%#llx to set %d\n", + (unsigned long long) new_option->u.port.min, + (unsigned long long) new_option->u.port.max, + pnp_option_set(option)); +} + static void quirk_awe32_resources(struct pnp_dev *dev) { - struct pnp_port *port, *port2, *port3; - struct pnp_option *res = dev->dependent; + struct pnp_option *option; + unsigned int set = ~0; /* - * Unfortunately the isapnp_add_port_resource is too tightly bound - * into the PnP discovery sequence, and cannot be used. Link in the - * two extra ports (at offset 0x400 and 0x800 from the one given) by - * hand. + * Add two extra ioport regions (at offset 0x400 and 0x800 from the + * one given) to every dependent option set. */ - for (; res; res = res->next) { - port2 = pnp_alloc(sizeof(struct pnp_port)); - if (!port2) - return; - port3 = pnp_alloc(sizeof(struct pnp_port)); - if (!port3) { - kfree(port2); - return; + list_for_each_entry(option, &dev->options, list) { + if (pnp_option_is_dependent(option) && + pnp_option_set(option) != set) { + set = pnp_option_set(option); + quirk_awe32_add_ports(dev, option, 0x800); + quirk_awe32_add_ports(dev, option, 0x400); } - port = res->port; - memcpy(port2, port, sizeof(struct pnp_port)); - memcpy(port3, port, sizeof(struct pnp_port)); - port->next = port2; - port2->next = port3; - port2->min += 0x400; - port2->max += 0x400; - port3->min += 0x800; - port3->max += 0x800; } - printk(KERN_INFO "pnp: AWE32 quirk - adding two ports\n"); } static void quirk_cmi8330_resources(struct pnp_dev *dev) { - struct pnp_option *res = dev->dependent; - unsigned long tmp; + struct pnp_option *option; + struct pnp_irq *irq; + struct pnp_dma *dma; - for (; res; res = res->next) { - - struct pnp_irq *irq; - struct pnp_dma *dma; - - for (irq = res->irq; irq; irq = irq->next) { // Valid irqs are 5, 7, 10 - tmp = 0x04A0; - bitmap_copy(irq->map, &tmp, 16); // 0000 0100 1010 0000 - } + list_for_each_entry(option, &dev->options, list) { + if (!pnp_option_is_dependent(option)) + continue; - for (dma = res->dma; dma; dma = dma->next) // Valid 8bit dma channels are 1,3 + if (option->type == IORESOURCE_IRQ) { + irq = &option->u.irq; + bitmap_zero(irq->map.bits, PNP_IRQ_NR); + __set_bit(5, irq->map.bits); + __set_bit(7, irq->map.bits); + __set_bit(10, irq->map.bits); + dev_info(&dev->dev, "set possible IRQs in " + "option set %d to 5, 7, 10\n", + pnp_option_set(option)); + } else if (option->type == IORESOURCE_DMA) { + dma = &option->u.dma; if ((dma->flags & IORESOURCE_DMA_TYPE_MASK) == - IORESOURCE_DMA_8BIT) - dma->map = 0x000A; + IORESOURCE_DMA_8BIT && + dma->map != 0x0A) { + dev_info(&dev->dev, "changing possible " + "DMA channel mask in option set %d " + "from %#02x to 0x0A (1, 3)\n", + pnp_option_set(option), dma->map); + dma->map = 0x0A; + } + } } - printk(KERN_INFO "pnp: CMI8330 quirk - fixing interrupts and dma\n"); } static void quirk_sb16audio_resources(struct pnp_dev *dev) { + struct pnp_option *option; + unsigned int prev_option_flags = ~0, n = 0; struct pnp_port *port; - struct pnp_option *res = dev->dependent; - int changed = 0; /* - * The default range on the mpu port for these devices is 0x388-0x388. + * The default range on the OPL port for these devices is 0x388-0x388. * Here we increase that range so that two such cards can be * auto-configured. */ + list_for_each_entry(option, &dev->options, list) { + if (prev_option_flags != option->flags) { + prev_option_flags = option->flags; + n = 0; + } - for (; res; res = res->next) { - port = res->port; - if (!port) - continue; - port = port->next; - if (!port) - continue; - port = port->next; - if (!port) - continue; - if (port->min != port->max) - continue; - port->max += 0x70; - changed = 1; + if (pnp_option_is_dependent(option) && + option->type == IORESOURCE_IO) { + n++; + port = &option->u.port; + if (n == 3 && port->min == port->max) { + port->max += 0x70; + dev_info(&dev->dev, "increased option port " + "range from %#llx-%#llx to " + "%#llx-%#llx\n", + (unsigned long long) port->min, + (unsigned long long) port->min, + (unsigned long long) port->min, + (unsigned long long) port->max); + } + } } - if (changed) - printk(KERN_INFO - "pnp: SB audio device quirk - increasing port range\n"); } +static struct pnp_option *pnp_clone_dependent_set(struct pnp_dev *dev, + unsigned int set) +{ + struct pnp_option *tail = NULL, *first_new_option = NULL; + struct pnp_option *option, *new_option; + unsigned int flags; + + list_for_each_entry(option, &dev->options, list) { + if (pnp_option_is_dependent(option)) + tail = option; + } + if (!tail) { + dev_err(&dev->dev, "no dependent option sets\n"); + return NULL; + } + + flags = pnp_new_dependent_set(dev, PNP_RES_PRIORITY_FUNCTIONAL); + list_for_each_entry(option, &dev->options, list) { + if (pnp_option_is_dependent(option) && + pnp_option_set(option) == set) { + new_option = kmalloc(sizeof(struct pnp_option), + GFP_KERNEL); + if (!new_option) { + dev_err(&dev->dev, "couldn't clone dependent " + "set %d\n", set); + return NULL; + } + + *new_option = *option; + new_option->flags = flags; + if (!first_new_option) + first_new_option = new_option; + + list_add(&new_option->list, &tail->list); + tail = new_option; + } + } + + return first_new_option; +} + + +static void quirk_add_irq_optional_dependent_sets(struct pnp_dev *dev) +{ + struct pnp_option *new_option; + unsigned int num_sets, i, set; + struct pnp_irq *irq; + + num_sets = dev->num_dependent_sets; + for (i = 0; i < num_sets; i++) { + new_option = pnp_clone_dependent_set(dev, i); + if (!new_option) + return; + + set = pnp_option_set(new_option); + while (new_option && pnp_option_set(new_option) == set) { + if (new_option->type == IORESOURCE_IRQ) { + irq = &new_option->u.irq; + irq->flags |= IORESOURCE_IRQ_OPTIONAL; + } + dbg_pnp_show_option(dev, new_option); + new_option = list_entry(new_option->list.next, + struct pnp_option, list); + } + + dev_info(&dev->dev, "added dependent option set %d (same as " + "set %d except IRQ optional)\n", set, i); + } +} + +static void quirk_ad1815_mpu_resources(struct pnp_dev *dev) +{ + struct pnp_option *option; + struct pnp_irq *irq = NULL; + unsigned int independent_irqs = 0; + + list_for_each_entry(option, &dev->options, list) { + if (option->type == IORESOURCE_IRQ && + !pnp_option_is_dependent(option)) { + independent_irqs++; + irq = &option->u.irq; + } + } + + if (independent_irqs != 1) + return; + + irq->flags |= IORESOURCE_IRQ_OPTIONAL; + dev_info(&dev->dev, "made independent IRQ optional\n"); +} #include <linux/pci.h> static void quirk_system_pci_resources(struct pnp_dev *dev) { struct pci_dev *pdev = NULL; + struct resource *res; resource_size_t pnp_start, pnp_end, pci_start, pci_end; int i, j; @@ -128,19 +246,22 @@ static void quirk_system_pci_resources(struct pnp_dev *dev) */ for_each_pci_dev(pdev) { for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { - if (!(pci_resource_flags(pdev, i) & IORESOURCE_MEM) || - pci_resource_len(pdev, i) == 0) + unsigned long type; + + type = pci_resource_flags(pdev, i) & + (IORESOURCE_IO | IORESOURCE_MEM); + if (!type || pci_resource_len(pdev, i) == 0) continue; pci_start = pci_resource_start(pdev, i); pci_end = pci_resource_end(pdev, i); - for (j = 0; j < PNP_MAX_MEM; j++) { - if (!pnp_mem_valid(dev, j) || - pnp_mem_len(dev, j) == 0) + for (j = 0; + (res = pnp_get_resource(dev, type, j)); j++) { + if (res->start == 0 && res->end == 0) continue; - pnp_start = pnp_mem_start(dev, j); - pnp_end = pnp_mem_end(dev, j); + pnp_start = res->start; + pnp_end = res->end; /* * If the PNP region doesn't overlap the PCI @@ -165,20 +286,130 @@ static void quirk_system_pci_resources(struct pnp_dev *dev) * the PCI region, and that might prevent a PCI * driver from requesting its resources. */ - dev_warn(&dev->dev, "mem resource " - "(0x%llx-0x%llx) overlaps %s BAR %d " - "(0x%llx-0x%llx), disabling\n", - (unsigned long long) pnp_start, - (unsigned long long) pnp_end, - pci_name(pdev), i, - (unsigned long long) pci_start, - (unsigned long long) pci_end); - pnp_mem_flags(dev, j) = 0; + dev_warn(&dev->dev, + "disabling %pR because it overlaps " + "%s BAR %d %pR\n", res, + pci_name(pdev), i, &pdev->resource[i]); + res->flags |= IORESOURCE_DISABLED; } } } } +#ifdef CONFIG_AMD_NB + +#include <asm/amd_nb.h> + +static void quirk_amd_mmconfig_area(struct pnp_dev *dev) +{ + resource_size_t start, end; + struct pnp_resource *pnp_res; + struct resource *res; + struct resource mmconfig_res, *mmconfig; + + mmconfig = amd_get_mmconfig_range(&mmconfig_res); + if (!mmconfig) + return; + + list_for_each_entry(pnp_res, &dev->resources, list) { + res = &pnp_res->res; + if (res->end < mmconfig->start || res->start > mmconfig->end || + (res->start == mmconfig->start && res->end == mmconfig->end)) + continue; + + dev_info(&dev->dev, FW_BUG + "%pR covers only part of AMD MMCONFIG area %pR; adding more reservations\n", + res, mmconfig); + if (mmconfig->start < res->start) { + start = mmconfig->start; + end = res->start - 1; + pnp_add_mem_resource(dev, start, end, 0); + } + if (mmconfig->end > res->end) { + start = res->end + 1; + end = mmconfig->end; + pnp_add_mem_resource(dev, start, end, 0); + } + break; + } +} +#endif + +#ifdef CONFIG_PCI +/* Device IDs of parts that have 32KB MCH space */ +static const unsigned int mch_quirk_devices[] = { + 0x0154, /* Ivy Bridge */ + 0x0c00, /* Haswell */ +}; + +static struct pci_dev *get_intel_host(void) +{ + int i; + struct pci_dev *host; + + for (i = 0; i < ARRAY_SIZE(mch_quirk_devices); i++) { + host = pci_get_device(PCI_VENDOR_ID_INTEL, mch_quirk_devices[i], + NULL); + if (host) + return host; + } + return NULL; +} + +static void quirk_intel_mch(struct pnp_dev *dev) +{ + struct pci_dev *host; + u32 addr_lo, addr_hi; + struct pci_bus_region region; + struct resource mch; + struct pnp_resource *pnp_res; + struct resource *res; + + host = get_intel_host(); + if (!host) + return; + + /* + * MCHBAR is not an architected PCI BAR, so MCH space is usually + * reported as a PNP0C02 resource. The MCH space was originally + * 16KB, but is 32KB in newer parts. Some BIOSes still report a + * PNP0C02 resource that is only 16KB, which means the rest of the + * MCH space is consumed but unreported. + */ + + /* + * Read MCHBAR for Host Member Mapped Register Range Base + * https://www-ssl.intel.com/content/www/us/en/processors/core/4th-gen-core-family-desktop-vol-2-datasheet + * Sec 3.1.12. + */ + pci_read_config_dword(host, 0x48, &addr_lo); + region.start = addr_lo & ~0x7fff; + pci_read_config_dword(host, 0x4c, &addr_hi); + region.start |= (u64) addr_hi << 32; + region.end = region.start + 32*1024 - 1; + + memset(&mch, 0, sizeof(mch)); + mch.flags = IORESOURCE_MEM; + pcibios_bus_to_resource(host->bus, &mch, ®ion); + + list_for_each_entry(pnp_res, &dev->resources, list) { + res = &pnp_res->res; + if (res->end < mch.start || res->start > mch.end) + continue; /* no overlap */ + if (res->start == mch.start && res->end == mch.end) + continue; /* exact match */ + + dev_info(&dev->dev, FW_BUG "PNP resource %pR covers only part of %s Intel MCH; extending to %pR\n", + res, pci_name(host), &mch); + res->start = mch.start; + res->end = mch.end; + break; + } + + pci_dev_put(host); +} +#endif + /* * PnP Quirks * Cards or devices that need some tweaking due to incomplete resource info @@ -199,27 +430,31 @@ static struct pnp_fixup pnp_fixups[] = { {"CTL0043", quirk_sb16audio_resources}, {"CTL0044", quirk_sb16audio_resources}, {"CTL0045", quirk_sb16audio_resources}, + /* Add IRQ-optional MPU options */ + {"ADS7151", quirk_ad1815_mpu_resources}, + {"ADS7181", quirk_add_irq_optional_dependent_sets}, + {"AZT0002", quirk_add_irq_optional_dependent_sets}, + /* PnP resources that might overlap PCI BARs */ {"PNP0c01", quirk_system_pci_resources}, {"PNP0c02", quirk_system_pci_resources}, +#ifdef CONFIG_AMD_NB + {"PNP0c01", quirk_amd_mmconfig_area}, +#endif +#ifdef CONFIG_PCI + {"PNP0c02", quirk_intel_mch}, +#endif {""} }; void pnp_fixup_device(struct pnp_dev *dev) { - int i = 0; - void (*quirk)(struct pnp_dev *); - - while (*pnp_fixups[i].id) { - if (compare_pnp_id(dev->id, pnp_fixups[i].id)) { - quirk = pnp_fixups[i].quirk_function; + struct pnp_fixup *f; -#ifdef DEBUG - dev_dbg(&dev->dev, "calling quirk 0x%p", quirk); - print_fn_descriptor_symbol(": %s()\n", - (unsigned long) *quirk); -#endif - (*quirk)(dev); - } - i++; + for (f = pnp_fixups; *f->id; f++) { + if (!compare_pnp_id(dev->id, f->id)) + continue; + pnp_dbg(&dev->dev, "%s: calling %pF\n", f->id, + f->quirk_function); + f->quirk_function(dev); } } diff --git a/drivers/pnp/resource.c b/drivers/pnp/resource.c index e50ebcffb96..782e8228957 100644 --- a/drivers/pnp/resource.c +++ b/drivers/pnp/resource.c @@ -3,9 +3,12 @@ * * 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> @@ -28,175 +31,121 @@ static int pnp_reserve_mem[16] = {[0 ... 15] = -1 }; /* reserve (don't use) some * option registration */ -static struct pnp_option *pnp_build_option(int priority) +static struct pnp_option *pnp_build_option(struct pnp_dev *dev, unsigned long type, + unsigned int option_flags) { - struct pnp_option *option = pnp_alloc(sizeof(struct pnp_option)); + struct pnp_option *option; + option = kzalloc(sizeof(struct pnp_option), GFP_KERNEL); if (!option) return NULL; - option->priority = priority & 0xff; - /* make sure the priority is valid */ - if (option->priority > PNP_RES_PRIORITY_FUNCTIONAL) - option->priority = PNP_RES_PRIORITY_INVALID; - - return option; -} - -struct pnp_option *pnp_register_independent_option(struct pnp_dev *dev) -{ - struct pnp_option *option; - - option = pnp_build_option(PNP_RES_PRIORITY_PREFERRED); + option->flags = option_flags; + option->type = type; - /* this should never happen but if it does we'll try to continue */ - if (dev->independent) - dev_err(&dev->dev, "independent resource already registered\n"); - dev->independent = option; + list_add_tail(&option->list, &dev->options); return option; } -struct pnp_option *pnp_register_dependent_option(struct pnp_dev *dev, - int priority) +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(priority); - - if (dev->dependent) { - struct pnp_option *parent = dev->dependent; - while (parent->next) - parent = parent->next; - parent->next = option; - } else - dev->dependent = option; - return option; -} - -int pnp_register_irq_resource(struct pnp_option *option, struct pnp_irq *data) -{ - struct pnp_irq *ptr; + option = pnp_build_option(dev, IORESOURCE_IRQ, option_flags); + if (!option) + return -ENOMEM; - ptr = option->irq; - while (ptr && ptr->next) - ptr = ptr->next; - if (ptr) - ptr->next = data; - else - option->irq = data; + 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, data->map)) + if (test_bit(i, irq->map.bits)) pcibios_penalize_isa_irq(i, 0); } #endif - return 0; -} - -int pnp_register_dma_resource(struct pnp_option *option, struct pnp_dma *data) -{ - struct pnp_dma *ptr; - - ptr = option->dma; - while (ptr && ptr->next) - ptr = ptr->next; - if (ptr) - ptr->next = data; - else - option->dma = data; + dbg_pnp_show_option(dev, option); return 0; } -int pnp_register_port_resource(struct pnp_option *option, struct pnp_port *data) +int pnp_register_dma_resource(struct pnp_dev *dev, unsigned int option_flags, + unsigned char map, unsigned char flags) { - struct pnp_port *ptr; + struct pnp_option *option; + struct pnp_dma *dma; - ptr = option->port; - while (ptr && ptr->next) - ptr = ptr->next; - if (ptr) - ptr->next = data; - else - option->port = data; + option = pnp_build_option(dev, IORESOURCE_DMA, option_flags); + if (!option) + return -ENOMEM; - return 0; -} + dma = &option->u.dma; + dma->map = map; + dma->flags = flags; -int pnp_register_mem_resource(struct pnp_option *option, struct pnp_mem *data) -{ - struct pnp_mem *ptr; - - ptr = option->mem; - while (ptr && ptr->next) - ptr = ptr->next; - if (ptr) - ptr->next = data; - else - option->mem = data; + dbg_pnp_show_option(dev, option); return 0; } -static void pnp_free_port(struct pnp_port *port) +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_port *next; + struct pnp_option *option; + struct pnp_port *port; - while (port) { - next = port->next; - kfree(port); - port = next; - } -} + option = pnp_build_option(dev, IORESOURCE_IO, option_flags); + if (!option) + return -ENOMEM; -static void pnp_free_irq(struct pnp_irq *irq) -{ - struct pnp_irq *next; + port = &option->u.port; + port->min = min; + port->max = max; + port->align = align; + port->size = size; + port->flags = flags; - while (irq) { - next = irq->next; - kfree(irq); - irq = next; - } + dbg_pnp_show_option(dev, option); + return 0; } -static void pnp_free_dma(struct pnp_dma *dma) +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_dma *next; + struct pnp_option *option; + struct pnp_mem *mem; - while (dma) { - next = dma->next; - kfree(dma); - dma = next; - } -} + option = pnp_build_option(dev, IORESOURCE_MEM, option_flags); + if (!option) + return -ENOMEM; -static void pnp_free_mem(struct pnp_mem *mem) -{ - struct pnp_mem *next; + mem = &option->u.mem; + mem->min = min; + mem->max = max; + mem->align = align; + mem->size = size; + mem->flags = flags; - while (mem) { - next = mem->next; - kfree(mem); - mem = next; - } + dbg_pnp_show_option(dev, option); + return 0; } -void pnp_free_option(struct pnp_option *option) +void pnp_free_options(struct pnp_dev *dev) { - struct pnp_option *next; - - while (option) { - next = option->next; - pnp_free_port(option->port); - pnp_free_irq(option->irq); - pnp_free_dma(option->dma); - pnp_free_mem(option->mem); + struct pnp_option *option, *tmp; + + list_for_each_entry_safe(option, tmp, &dev->options, list) { + list_del(&option->list); kfree(option); - option = next; } } @@ -211,19 +160,20 @@ void pnp_free_option(struct pnp_option *option) !((*(enda) < *(startb)) || (*(endb) < *(starta))) #define cannot_compare(flags) \ -((flags) & (IORESOURCE_UNSET | IORESOURCE_DISABLED)) +((flags) & IORESOURCE_DISABLED) -int pnp_check_port(struct pnp_dev *dev, int idx) +int pnp_check_port(struct pnp_dev *dev, struct resource *res) { - int tmp; + int i; struct pnp_dev *tdev; + struct resource *tres; resource_size_t *port, *end, *tport, *tend; - port = &dev->res.port_resource[idx].start; - end = &dev->res.port_resource[idx].end; + port = &res->start; + end = &res->end; /* if the resource doesn't exist, don't complain about it */ - if (cannot_compare(dev->res.port_resource[idx].flags)) + if (cannot_compare(res->flags)) return 1; /* check if the resource is already in use, skip if the @@ -234,18 +184,18 @@ int pnp_check_port(struct pnp_dev *dev, int idx) } /* check if the resource is reserved */ - for (tmp = 0; tmp < 8; tmp++) { - int rport = pnp_reserve_io[tmp << 1]; - int rend = pnp_reserve_io[(tmp << 1) + 1] + rport - 1; + 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 (tmp = 0; tmp < PNP_MAX_PORT && tmp != idx; tmp++) { - if (dev->res.port_resource[tmp].flags & IORESOURCE_IO) { - tport = &dev->res.port_resource[tmp].start; - tend = &dev->res.port_resource[tmp].end; + 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; } @@ -255,13 +205,16 @@ int pnp_check_port(struct pnp_dev *dev, int idx) pnp_for_each_dev(tdev) { if (tdev == dev) continue; - for (tmp = 0; tmp < PNP_MAX_PORT; tmp++) { - if (tdev->res.port_resource[tmp].flags & IORESOURCE_IO) { - if (cannot_compare - (tdev->res.port_resource[tmp].flags)) + for (i = 0; + (tres = pnp_get_resource(tdev, IORESOURCE_IO, i)); + i++) { + if (tres->flags & IORESOURCE_IO) { + if (cannot_compare(tres->flags)) continue; - tport = &tdev->res.port_resource[tmp].start; - tend = &tdev->res.port_resource[tmp].end; + if (tres->flags & IORESOURCE_WINDOW) + continue; + tport = &tres->start; + tend = &tres->end; if (ranged_conflict(port, end, tport, tend)) return 0; } @@ -271,17 +224,18 @@ int pnp_check_port(struct pnp_dev *dev, int idx) return 1; } -int pnp_check_mem(struct pnp_dev *dev, int idx) +int pnp_check_mem(struct pnp_dev *dev, struct resource *res) { - int tmp; + int i; struct pnp_dev *tdev; + struct resource *tres; resource_size_t *addr, *end, *taddr, *tend; - addr = &dev->res.mem_resource[idx].start; - end = &dev->res.mem_resource[idx].end; + addr = &res->start; + end = &res->end; /* if the resource doesn't exist, don't complain about it */ - if (cannot_compare(dev->res.mem_resource[idx].flags)) + if (cannot_compare(res->flags)) return 1; /* check if the resource is already in use, skip if the @@ -292,18 +246,18 @@ int pnp_check_mem(struct pnp_dev *dev, int idx) } /* check if the resource is reserved */ - for (tmp = 0; tmp < 8; tmp++) { - int raddr = pnp_reserve_mem[tmp << 1]; - int rend = pnp_reserve_mem[(tmp << 1) + 1] + raddr - 1; + 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 (tmp = 0; tmp < PNP_MAX_MEM && tmp != idx; tmp++) { - if (dev->res.mem_resource[tmp].flags & IORESOURCE_MEM) { - taddr = &dev->res.mem_resource[tmp].start; - tend = &dev->res.mem_resource[tmp].end; + 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; } @@ -313,13 +267,16 @@ int pnp_check_mem(struct pnp_dev *dev, int idx) pnp_for_each_dev(tdev) { if (tdev == dev) continue; - for (tmp = 0; tmp < PNP_MAX_MEM; tmp++) { - if (tdev->res.mem_resource[tmp].flags & IORESOURCE_MEM) { - if (cannot_compare - (tdev->res.mem_resource[tmp].flags)) + 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 = &tdev->res.mem_resource[tmp].start; - tend = &tdev->res.mem_resource[tmp].end; + taddr = &tres->start; + tend = &tres->end; if (ranged_conflict(addr, end, taddr, tend)) return 0; } @@ -334,52 +291,101 @@ static irqreturn_t pnp_test_handler(int irq, void *dev_id) return IRQ_HANDLED; } -int pnp_check_irq(struct pnp_dev *dev, int idx) +#ifdef CONFIG_PCI +static 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 (pci_get_legacy_ide_irq(pci, 0) == irq || + pci_get_legacy_ide_irq(pci, 1) == 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) { - int tmp; +#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; - resource_size_t *irq = &dev->res.irq_resource[idx].start; + struct resource *tres; + resource_size_t *irq; + + irq = &res->start; /* if the resource doesn't exist, don't complain about it */ - if (cannot_compare(dev->res.irq_resource[idx].flags)) + if (cannot_compare(res->flags)) return 1; /* check if the resource is valid */ - if (*irq < 0 || *irq > 15) + if (*irq > 15) return 0; /* check if the resource is reserved */ - for (tmp = 0; tmp < 16; tmp++) { - if (pnp_reserve_irq[tmp] == *irq) + for (i = 0; i < 16; i++) { + if (pnp_reserve_irq[i] == *irq) return 0; } /* check for internal conflicts */ - for (tmp = 0; tmp < PNP_MAX_IRQ && tmp != idx; tmp++) { - if (dev->res.irq_resource[tmp].flags & IORESOURCE_IRQ) { - if (dev->res.irq_resource[tmp].start == *irq) + 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; } } -#ifdef CONFIG_PCI /* check if the resource is being used by a pci device */ - { - struct pci_dev *pci = NULL; - for_each_pci_dev(pci) { - if (pci->irq == *irq) { - pci_dev_put(pci); - return 0; - } - } - } -#endif + 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_DISABLED | IRQF_PROBE_SHARED, "pnp", NULL)) + IRQF_PROBE_SHARED, "pnp", NULL)) return 0; free_irq(*irq, NULL); } @@ -388,12 +394,13 @@ int pnp_check_irq(struct pnp_dev *dev, int idx) pnp_for_each_dev(tdev) { if (tdev == dev) continue; - for (tmp = 0; tmp < PNP_MAX_IRQ; tmp++) { - if (tdev->res.irq_resource[tmp].flags & IORESOURCE_IRQ) { - if (cannot_compare - (tdev->res.irq_resource[tmp].flags)) + for (i = 0; + (tres = pnp_get_resource(tdev, IORESOURCE_IRQ, i)); + i++) { + if (tres->flags & IORESOURCE_IRQ) { + if (cannot_compare(tres->flags)) continue; - if ((tdev->res.irq_resource[tmp].start == *irq)) + if (tres->start == *irq) return 0; } } @@ -402,31 +409,34 @@ int pnp_check_irq(struct pnp_dev *dev, int idx) return 1; } -int pnp_check_dma(struct pnp_dev *dev, int idx) +#ifdef CONFIG_ISA_DMA_API +int pnp_check_dma(struct pnp_dev *dev, struct resource *res) { -#ifndef CONFIG_IA64 - int tmp; + int i; struct pnp_dev *tdev; - resource_size_t *dma = &dev->res.dma_resource[idx].start; + struct resource *tres; + resource_size_t *dma; + + dma = &res->start; /* if the resource doesn't exist, don't complain about it */ - if (cannot_compare(dev->res.dma_resource[idx].flags)) + if (cannot_compare(res->flags)) return 1; /* check if the resource is valid */ - if (*dma < 0 || *dma == 4 || *dma > 7) + if (*dma == 4 || *dma > 7) return 0; /* check if the resource is reserved */ - for (tmp = 0; tmp < 8; tmp++) { - if (pnp_reserve_dma[tmp] == *dma) + for (i = 0; i < 8; i++) { + if (pnp_reserve_dma[i] == *dma) return 0; } /* check for internal conflicts */ - for (tmp = 0; tmp < PNP_MAX_DMA && tmp != idx; tmp++) { - if (dev->res.dma_resource[tmp].flags & IORESOURCE_DMA) { - if (dev->res.dma_resource[tmp].start == *dma) + 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; } } @@ -443,23 +453,250 @@ int pnp_check_dma(struct pnp_dev *dev, int idx) pnp_for_each_dev(tdev) { if (tdev == dev) continue; - for (tmp = 0; tmp < PNP_MAX_DMA; tmp++) { - if (tdev->res.dma_resource[tmp].flags & IORESOURCE_DMA) { - if (cannot_compare - (tdev->res.dma_resource[tmp].flags)) + for (i = 0; + (tres = pnp_get_resource(tdev, IORESOURCE_DMA, i)); + i++) { + if (tres->flags & IORESOURCE_DMA) { + if (cannot_compare(tres->flags)) continue; - if ((tdev->res.dma_resource[tmp].start == *dma)) + if (tres->start == *dma) return 0; } } } return 1; -#else - /* IA64 does not have legacy DMA */ +} +#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; + + pnp_res = kzalloc(sizeof(struct pnp_resource), GFP_KERNEL); + if (!pnp_res) + return NULL; + + list_add_tail(&pnp_res->list, &dev->resources); + return pnp_res; +} + +struct pnp_resource *pnp_add_resource(struct pnp_dev *dev, + struct resource *res) +{ + struct pnp_resource *pnp_res; + + pnp_res = pnp_new_resource(dev); + if (!pnp_res) { + dev_err(&dev->dev, "can't add resource %pR\n", res); + return NULL; + } + + pnp_res->res = *res; + pnp_res->res.name = dev->name; + dev_dbg(&dev->dev, "%pR\n", res); + return pnp_res; +} + +struct pnp_resource *pnp_add_irq_resource(struct pnp_dev *dev, int irq, + int flags) +{ + struct pnp_resource *pnp_res; + struct resource *res; + + pnp_res = pnp_new_resource(dev); + if (!pnp_res) { + dev_err(&dev->dev, "can't add resource for IRQ %d\n", irq); + return NULL; + } + + res = &pnp_res->res; + res->flags = IORESOURCE_IRQ | flags; + res->start = irq; + res->end = irq; + + dev_printk(KERN_DEBUG, &dev->dev, "%pR\n", res); + return pnp_res; +} + +struct pnp_resource *pnp_add_dma_resource(struct pnp_dev *dev, int dma, + int flags) +{ + struct pnp_resource *pnp_res; + struct resource *res; + + pnp_res = pnp_new_resource(dev); + if (!pnp_res) { + dev_err(&dev->dev, "can't add resource for DMA %d\n", dma); + return NULL; + } + + res = &pnp_res->res; + res->flags = IORESOURCE_DMA | flags; + res->start = dma; + res->end = dma; + + dev_printk(KERN_DEBUG, &dev->dev, "%pR\n", res); + return pnp_res; +} + +struct pnp_resource *pnp_add_io_resource(struct pnp_dev *dev, + resource_size_t start, + resource_size_t end, int flags) +{ + struct pnp_resource *pnp_res; + struct resource *res; + + pnp_res = pnp_new_resource(dev); + if (!pnp_res) { + dev_err(&dev->dev, "can't add resource for IO %#llx-%#llx\n", + (unsigned long long) start, + (unsigned long long) end); + return NULL; + } + + res = &pnp_res->res; + res->flags = IORESOURCE_IO | flags; + res->start = start; + res->end = end; + + dev_printk(KERN_DEBUG, &dev->dev, "%pR\n", res); + return pnp_res; +} + +struct pnp_resource *pnp_add_mem_resource(struct pnp_dev *dev, + resource_size_t start, + resource_size_t end, int flags) +{ + struct pnp_resource *pnp_res; + struct resource *res; + + pnp_res = pnp_new_resource(dev); + if (!pnp_res) { + dev_err(&dev->dev, "can't add resource for MEM %#llx-%#llx\n", + (unsigned long long) start, + (unsigned long long) end); + return NULL; + } + + res = &pnp_res->res; + res->flags = IORESOURCE_MEM | flags; + res->start = start; + res->end = end; + + dev_printk(KERN_DEBUG, &dev->dev, "%pR\n", res); + return pnp_res; +} + +struct pnp_resource *pnp_add_bus_resource(struct pnp_dev *dev, + resource_size_t start, + resource_size_t end) +{ + struct pnp_resource *pnp_res; + struct resource *res; + + pnp_res = pnp_new_resource(dev); + if (!pnp_res) { + dev_err(&dev->dev, "can't add resource for BUS %#llx-%#llx\n", + (unsigned long long) start, + (unsigned long long) end); + return NULL; + } + + res = &pnp_res->res; + res->flags = IORESOURCE_BUS; + res->start = start; + res->end = end; + + dev_printk(KERN_DEBUG, &dev->dev, "%pR\n", res); + return pnp_res; +} + +/* + * Determine whether the specified resource is a possible configuration + * for this device. + */ +int pnp_possible_config(struct pnp_dev *dev, int type, resource_size_t start, + resource_size_t size) +{ + struct pnp_option *option; + struct pnp_port *port; + struct pnp_mem *mem; + struct pnp_irq *irq; + struct pnp_dma *dma; + + list_for_each_entry(option, &dev->options, list) { + if (option->type != type) + continue; + + switch (option->type) { + case IORESOURCE_IO: + port = &option->u.port; + if (port->min == start && port->size == size) + return 1; + break; + case IORESOURCE_MEM: + mem = &option->u.mem; + if (mem->min == start && mem->size == size) + return 1; + break; + case IORESOURCE_IRQ: + irq = &option->u.irq; + if (start < PNP_IRQ_NR && + test_bit(start, irq->map.bits)) + return 1; + break; + case IORESOURCE_DMA: + dma = &option->u.dma; + if (dma->map & (1 << start)) + return 1; + break; + } + } + + return 0; +} +EXPORT_SYMBOL(pnp_possible_config); + +int pnp_range_reserved(resource_size_t start, resource_size_t end) +{ + struct pnp_dev *dev; + struct pnp_resource *pnp_res; + resource_size_t *dev_start, *dev_end; + + pnp_for_each_dev(dev) { + list_for_each_entry(pnp_res, &dev->resources, list) { + dev_start = &pnp_res->res.start; + dev_end = &pnp_res->res.end; + if (ranged_conflict(&start, &end, dev_start, dev_end)) + return 1; + } + } return 0; -#endif } +EXPORT_SYMBOL(pnp_range_reserved); /* format is: pnp_reserve_irq=irq1[,irq2] .... */ static int __init pnp_setup_reserve_irq(char *str) diff --git a/drivers/pnp/support.c b/drivers/pnp/support.c index 13c608f5fb3..f5beb24d036 100644 --- a/drivers/pnp/support.c +++ b/drivers/pnp/support.c @@ -2,6 +2,8 @@ * support.c - standard functions for the use of pnp protocol drivers * * 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> @@ -16,6 +18,10 @@ */ int pnp_is_active(struct pnp_dev *dev) { + /* + * I don't think this is very reliable because pnp_disable_dev() + * only clears out auto-assigned resources. + */ if (!pnp_port_start(dev, 0) && pnp_port_len(dev, 0) <= 1 && !pnp_mem_start(dev, 0) && pnp_mem_len(dev, 0) <= 1 && pnp_irq(dev, 0) == -1 && pnp_dma(dev, 0) == -1) @@ -25,3 +31,148 @@ int pnp_is_active(struct pnp_dev *dev) } EXPORT_SYMBOL(pnp_is_active); + +/* + * Functionally similar to acpi_ex_eisa_id_to_string(), but that's + * buried in the ACPI CA, and we can't depend on it being present. + */ +void pnp_eisa_id_to_string(u32 id, char *str) +{ + id = be32_to_cpu(id); + + /* + * According to the specs, the first three characters are five-bit + * compressed ASCII, and the left-over high order bit should be zero. + * However, the Linux ISAPNP code historically used six bits for the + * first character, and there seem to be IDs that depend on that, + * e.g., "nEC8241" in the Linux 8250_pnp serial driver and the + * FreeBSD sys/pc98/cbus/sio_cbus.c driver. + */ + str[0] = 'A' + ((id >> 26) & 0x3f) - 1; + str[1] = 'A' + ((id >> 21) & 0x1f) - 1; + str[2] = 'A' + ((id >> 16) & 0x1f) - 1; + str[3] = hex_asc_hi(id >> 8); + str[4] = hex_asc_lo(id >> 8); + str[5] = hex_asc_hi(id); + str[6] = hex_asc_lo(id); + str[7] = '\0'; +} + +char *pnp_resource_type_name(struct resource *res) +{ + switch (pnp_resource_type(res)) { + case IORESOURCE_IO: + return "io"; + case IORESOURCE_MEM: + return "mem"; + case IORESOURCE_IRQ: + return "irq"; + case IORESOURCE_DMA: + return "dma"; + case IORESOURCE_BUS: + return "bus"; + } + return "unknown"; +} + +void dbg_pnp_show_resources(struct pnp_dev *dev, char *desc) +{ + struct pnp_resource *pnp_res; + + if (list_empty(&dev->resources)) + pnp_dbg(&dev->dev, "%s: no current resources\n", desc); + else { + pnp_dbg(&dev->dev, "%s: current resources:\n", desc); + list_for_each_entry(pnp_res, &dev->resources, list) + pnp_dbg(&dev->dev, "%pr\n", &pnp_res->res); + } +} + +char *pnp_option_priority_name(struct pnp_option *option) +{ + switch (pnp_option_priority(option)) { + case PNP_RES_PRIORITY_PREFERRED: + return "preferred"; + case PNP_RES_PRIORITY_ACCEPTABLE: + return "acceptable"; + case PNP_RES_PRIORITY_FUNCTIONAL: + return "functional"; + } + return "invalid"; +} + +void dbg_pnp_show_option(struct pnp_dev *dev, struct pnp_option *option) +{ + char buf[128]; + int len = 0, i; + struct pnp_port *port; + struct pnp_mem *mem; + struct pnp_irq *irq; + struct pnp_dma *dma; + + if (pnp_option_is_dependent(option)) + len += scnprintf(buf + len, sizeof(buf) - len, + " dependent set %d (%s) ", + pnp_option_set(option), + pnp_option_priority_name(option)); + else + len += scnprintf(buf + len, sizeof(buf) - len, + " independent "); + + switch (option->type) { + case IORESOURCE_IO: + port = &option->u.port; + len += scnprintf(buf + len, sizeof(buf) - len, "io min %#llx " + "max %#llx align %lld size %lld flags %#x", + (unsigned long long) port->min, + (unsigned long long) port->max, + (unsigned long long) port->align, + (unsigned long long) port->size, port->flags); + break; + case IORESOURCE_MEM: + mem = &option->u.mem; + len += scnprintf(buf + len, sizeof(buf) - len, "mem min %#llx " + "max %#llx align %lld size %lld flags %#x", + (unsigned long long) mem->min, + (unsigned long long) mem->max, + (unsigned long long) mem->align, + (unsigned long long) mem->size, mem->flags); + break; + case IORESOURCE_IRQ: + irq = &option->u.irq; + len += scnprintf(buf + len, sizeof(buf) - len, "irq"); + if (bitmap_empty(irq->map.bits, PNP_IRQ_NR)) + len += scnprintf(buf + len, sizeof(buf) - len, + " <none>"); + else { + for (i = 0; i < PNP_IRQ_NR; i++) + if (test_bit(i, irq->map.bits)) + len += scnprintf(buf + len, + sizeof(buf) - len, + " %d", i); + } + len += scnprintf(buf + len, sizeof(buf) - len, " flags %#x", + irq->flags); + if (irq->flags & IORESOURCE_IRQ_OPTIONAL) + len += scnprintf(buf + len, sizeof(buf) - len, + " (optional)"); + break; + case IORESOURCE_DMA: + dma = &option->u.dma; + len += scnprintf(buf + len, sizeof(buf) - len, "dma"); + if (!dma->map) + len += scnprintf(buf + len, sizeof(buf) - len, + " <none>"); + else { + for (i = 0; i < 8; i++) + if (dma->map & (1 << i)) + len += scnprintf(buf + len, + sizeof(buf) - len, + " %d", i); + } + len += scnprintf(buf + len, sizeof(buf) - len, " (bitmask %#x) " + "flags %#x", dma->map, dma->flags); + break; + } + pnp_dbg(&dev->dev, "%s\n", buf); +} diff --git a/drivers/pnp/system.c b/drivers/pnp/system.c index 55c4563986b..49c1720df59 100644 --- a/drivers/pnp/system.c +++ b/drivers/pnp/system.c @@ -22,11 +22,11 @@ static const struct pnp_device_id pnp_dev_table[] = { {"", 0} }; -static void reserve_range(struct pnp_dev *dev, resource_size_t start, - resource_size_t end, int port) +static void reserve_range(struct pnp_dev *dev, struct resource *r, int port) { char *regionid; - const char *pnpid = dev->dev.bus_id; + const char *pnpid = dev_name(&dev->dev); + resource_size_t start = r->start, end = r->end; struct resource *res; regionid = kmalloc(16, GFP_KERNEL); @@ -48,22 +48,21 @@ static void reserve_range(struct pnp_dev *dev, resource_size_t start, * example do reserve stuff they know about too, so we may well * have double reservations. */ - dev_info(&dev->dev, "%s range 0x%llx-0x%llx %s reserved\n", - port ? "ioport" : "iomem", - (unsigned long long) start, (unsigned long long) end, - res ? "has been" : "could not be"); + dev_info(&dev->dev, "%pR %s reserved\n", r, + res ? "has been" : "could not be"); } static void reserve_resources_of_dev(struct pnp_dev *dev) { + struct resource *res; int i; - for (i = 0; i < PNP_MAX_PORT; i++) { - if (!pnp_port_valid(dev, i)) + for (i = 0; (res = pnp_get_resource(dev, IORESOURCE_IO, i)); i++) { + if (res->flags & IORESOURCE_DISABLED) continue; - if (pnp_port_start(dev, i) == 0) + if (res->start == 0) continue; /* disabled */ - if (pnp_port_start(dev, i) < 0x100) + if (res->start < 0x100) /* * Below 0x100 is only standard PC hardware * (pics, kbd, timer, dma, ...) @@ -73,19 +72,17 @@ static void reserve_resources_of_dev(struct pnp_dev *dev) * So, do nothing */ continue; - if (pnp_port_end(dev, i) < pnp_port_start(dev, i)) + if (res->end < res->start) continue; /* invalid */ - reserve_range(dev, pnp_port_start(dev, i), - pnp_port_end(dev, i), 1); + reserve_range(dev, res, 1); } - for (i = 0; i < PNP_MAX_MEM; i++) { - if (!pnp_mem_valid(dev, i)) + for (i = 0; (res = pnp_get_resource(dev, IORESOURCE_MEM, i)); i++) { + if (res->flags & IORESOURCE_DISABLED) continue; - reserve_range(dev, pnp_mem_start(dev, i), - pnp_mem_end(dev, i), 0); + reserve_range(dev, res, 0); } } |
