linux/drivers/pnp/manager.c
<<
>>
Prefs
   1/*
   2 * manager.c - Resource Management, Conflict Resolution, Activation and Disabling of Devices
   3 *
   4 * based on isapnp.c resource management (c) Jaroslav Kysela <perex@perex.cz>
   5 * Copyright 2003 Adam Belay <ambx1@neo.rr.com>
   6 * Copyright (C) 2008 Hewlett-Packard Development Company, L.P.
   7 *      Bjorn Helgaas <bjorn.helgaas@hp.com>
   8 */
   9
  10#include <linux/errno.h>
  11#include <linux/module.h>
  12#include <linux/init.h>
  13#include <linux/kernel.h>
  14#include <linux/pnp.h>
  15#include <linux/bitmap.h>
  16#include <linux/mutex.h>
  17#include "base.h"
  18
  19DEFINE_MUTEX(pnp_res_mutex);
  20
  21static int pnp_assign_port(struct pnp_dev *dev, struct pnp_port *rule, int idx)
  22{
  23        struct resource *res, local_res;
  24
  25        res = pnp_get_resource(dev, IORESOURCE_IO, idx);
  26        if (res) {
  27                pnp_dbg(&dev->dev, "  io %d already set to %#llx-%#llx "
  28                        "flags %#lx\n", idx, (unsigned long long) res->start,
  29                        (unsigned long long) res->end, res->flags);
  30                return 0;
  31        }
  32
  33        res = &local_res;
  34        res->flags = rule->flags | IORESOURCE_AUTO;
  35        res->start = 0;
  36        res->end = 0;
  37
  38        if (!rule->size) {
  39                res->flags |= IORESOURCE_DISABLED;
  40                pnp_dbg(&dev->dev, "  io %d disabled\n", idx);
  41                goto __add;
  42        }
  43
  44        res->start = rule->min;
  45        res->end = res->start + rule->size - 1;
  46
  47        while (!pnp_check_port(dev, res)) {
  48                res->start += rule->align;
  49                res->end = res->start + rule->size - 1;
  50                if (res->start > rule->max || !rule->align) {
  51                        pnp_dbg(&dev->dev, "  couldn't assign io %d "
  52                                "(min %#llx max %#llx)\n", idx,
  53                                (unsigned long long) rule->min,
  54                                (unsigned long long) rule->max);
  55                        return -EBUSY;
  56                }
  57        }
  58
  59__add:
  60        pnp_add_io_resource(dev, res->start, res->end, res->flags);
  61        return 0;
  62}
  63
  64static int pnp_assign_mem(struct pnp_dev *dev, struct pnp_mem *rule, int idx)
  65{
  66        struct resource *res, local_res;
  67
  68        res = pnp_get_resource(dev, IORESOURCE_MEM, idx);
  69        if (res) {
  70                pnp_dbg(&dev->dev, "  mem %d already set to %#llx-%#llx "
  71                        "flags %#lx\n", idx, (unsigned long long) res->start,
  72                        (unsigned long long) res->end, res->flags);
  73                return 0;
  74        }
  75
  76        res = &local_res;
  77        res->flags = rule->flags | IORESOURCE_AUTO;
  78        res->start = 0;
  79        res->end = 0;
  80
  81        if (!(rule->flags & IORESOURCE_MEM_WRITEABLE))
  82                res->flags |= IORESOURCE_READONLY;
  83        if (rule->flags & IORESOURCE_MEM_CACHEABLE)
  84                res->flags |= IORESOURCE_CACHEABLE;
  85        if (rule->flags & IORESOURCE_MEM_RANGELENGTH)
  86                res->flags |= IORESOURCE_RANGELENGTH;
  87        if (rule->flags & IORESOURCE_MEM_SHADOWABLE)
  88                res->flags |= IORESOURCE_SHADOWABLE;
  89
  90        if (!rule->size) {
  91                res->flags |= IORESOURCE_DISABLED;
  92                pnp_dbg(&dev->dev, "  mem %d disabled\n", idx);
  93                goto __add;
  94        }
  95
  96        res->start = rule->min;
  97        res->end = res->start + rule->size - 1;
  98
  99        while (!pnp_check_mem(dev, res)) {
 100                res->start += rule->align;
 101                res->end = res->start + rule->size - 1;
 102                if (res->start > rule->max || !rule->align) {
 103                        pnp_dbg(&dev->dev, "  couldn't assign mem %d "
 104                                "(min %#llx max %#llx)\n", idx,
 105                                (unsigned long long) rule->min,
 106                                (unsigned long long) rule->max);
 107                        return -EBUSY;
 108                }
 109        }
 110
 111__add:
 112        pnp_add_mem_resource(dev, res->start, res->end, res->flags);
 113        return 0;
 114}
 115
 116static int pnp_assign_irq(struct pnp_dev *dev, struct pnp_irq *rule, int idx)
 117{
 118        struct resource *res, local_res;
 119        int i;
 120
 121        /* IRQ priority: this table is good for i386 */
 122        static unsigned short xtab[16] = {
 123                5, 10, 11, 12, 9, 14, 15, 7, 3, 4, 13, 0, 1, 6, 8, 2
 124        };
 125
 126        res = pnp_get_resource(dev, IORESOURCE_IRQ, idx);
 127        if (res) {
 128                pnp_dbg(&dev->dev, "  irq %d already set to %d flags %#lx\n",
 129                        idx, (int) res->start, res->flags);
 130                return 0;
 131        }
 132
 133        res = &local_res;
 134        res->flags = rule->flags | IORESOURCE_AUTO;
 135        res->start = -1;
 136        res->end = -1;
 137
 138        if (bitmap_empty(rule->map.bits, PNP_IRQ_NR)) {
 139                res->flags |= IORESOURCE_DISABLED;
 140                pnp_dbg(&dev->dev, "  irq %d disabled\n", idx);
 141                goto __add;
 142        }
 143
 144        /* TBD: need check for >16 IRQ */
 145        res->start = find_next_bit(rule->map.bits, PNP_IRQ_NR, 16);
 146        if (res->start < PNP_IRQ_NR) {
 147                res->end = res->start;
 148                goto __add;
 149        }
 150        for (i = 0; i < 16; i++) {
 151                if (test_bit(xtab[i], rule->map.bits)) {
 152                        res->start = res->end = xtab[i];
 153                        if (pnp_check_irq(dev, res))
 154                                goto __add;
 155                }
 156        }
 157
 158        if (rule->flags & IORESOURCE_IRQ_OPTIONAL) {
 159                res->start = -1;
 160                res->end = -1;
 161                res->flags |= IORESOURCE_DISABLED;
 162                pnp_dbg(&dev->dev, "  irq %d disabled (optional)\n", idx);
 163                goto __add;
 164        }
 165
 166        pnp_dbg(&dev->dev, "  couldn't assign irq %d\n", idx);
 167        return -EBUSY;
 168
 169__add:
 170        pnp_add_irq_resource(dev, res->start, res->flags);
 171        return 0;
 172}
 173
 174#ifdef CONFIG_ISA_DMA_API
 175static int pnp_assign_dma(struct pnp_dev *dev, struct pnp_dma *rule, int idx)
 176{
 177        struct resource *res, local_res;
 178        int i;
 179
 180        /* DMA priority: this table is good for i386 */
 181        static unsigned short xtab[8] = {
 182                1, 3, 5, 6, 7, 0, 2, 4
 183        };
 184
 185        res = pnp_get_resource(dev, IORESOURCE_DMA, idx);
 186        if (res) {
 187                pnp_dbg(&dev->dev, "  dma %d already set to %d flags %#lx\n",
 188                        idx, (int) res->start, res->flags);
 189                return 0;
 190        }
 191
 192        res = &local_res;
 193        res->flags = rule->flags | IORESOURCE_AUTO;
 194        res->start = -1;
 195        res->end = -1;
 196
 197        for (i = 0; i < 8; i++) {
 198                if (rule->map & (1 << xtab[i])) {
 199                        res->start = res->end = xtab[i];
 200                        if (pnp_check_dma(dev, res))
 201                                goto __add;
 202                }
 203        }
 204#ifdef MAX_DMA_CHANNELS
 205        res->start = res->end = MAX_DMA_CHANNELS;
 206#endif
 207        res->flags |= IORESOURCE_DISABLED;
 208        pnp_dbg(&dev->dev, "  disable dma %d\n", idx);
 209
 210__add:
 211        pnp_add_dma_resource(dev, res->start, res->flags);
 212        return 0;
 213}
 214#endif /* CONFIG_ISA_DMA_API */
 215
 216void pnp_init_resources(struct pnp_dev *dev)
 217{
 218        pnp_free_resources(dev);
 219}
 220
 221static void pnp_clean_resource_table(struct pnp_dev *dev)
 222{
 223        struct pnp_resource *pnp_res, *tmp;
 224
 225        list_for_each_entry_safe(pnp_res, tmp, &dev->resources, list) {
 226                if (pnp_res->res.flags & IORESOURCE_AUTO)
 227                        pnp_free_resource(pnp_res);
 228        }
 229}
 230
 231/**
 232 * pnp_assign_resources - assigns resources to the device based on the specified dependent number
 233 * @dev: pointer to the desired device
 234 * @set: the dependent function number
 235 */
 236static int pnp_assign_resources(struct pnp_dev *dev, int set)
 237{
 238        struct pnp_option *option;
 239        int nport = 0, nmem = 0, nirq = 0;
 240        int ndma __maybe_unused = 0;
 241        int ret = 0;
 242
 243        pnp_dbg(&dev->dev, "pnp_assign_resources, try dependent set %d\n", set);
 244        mutex_lock(&pnp_res_mutex);
 245        pnp_clean_resource_table(dev);
 246
 247        list_for_each_entry(option, &dev->options, list) {
 248                if (pnp_option_is_dependent(option) &&
 249                    pnp_option_set(option) != set)
 250                                continue;
 251
 252                switch (option->type) {
 253                case IORESOURCE_IO:
 254                        ret = pnp_assign_port(dev, &option->u.port, nport++);
 255                        break;
 256                case IORESOURCE_MEM:
 257                        ret = pnp_assign_mem(dev, &option->u.mem, nmem++);
 258                        break;
 259                case IORESOURCE_IRQ:
 260                        ret = pnp_assign_irq(dev, &option->u.irq, nirq++);
 261                        break;
 262#ifdef CONFIG_ISA_DMA_API
 263                case IORESOURCE_DMA:
 264                        ret = pnp_assign_dma(dev, &option->u.dma, ndma++);
 265                        break;
 266#endif
 267                default:
 268                        ret = -EINVAL;
 269                        break;
 270                }
 271                if (ret < 0)
 272                        break;
 273        }
 274
 275        mutex_unlock(&pnp_res_mutex);
 276        if (ret < 0) {
 277                pnp_dbg(&dev->dev, "pnp_assign_resources failed (%d)\n", ret);
 278                pnp_clean_resource_table(dev);
 279        } else
 280                dbg_pnp_show_resources(dev, "pnp_assign_resources succeeded");
 281        return ret;
 282}
 283
 284/**
 285 * pnp_auto_config_dev - automatically assigns resources to a device
 286 * @dev: pointer to the desired device
 287 */
 288int pnp_auto_config_dev(struct pnp_dev *dev)
 289{
 290        int i, ret;
 291
 292        if (!pnp_can_configure(dev)) {
 293                pnp_dbg(&dev->dev, "configuration not supported\n");
 294                return -ENODEV;
 295        }
 296
 297        ret = pnp_assign_resources(dev, 0);
 298        if (ret == 0)
 299                return 0;
 300
 301        for (i = 1; i < dev->num_dependent_sets; i++) {
 302                ret = pnp_assign_resources(dev, i);
 303                if (ret == 0)
 304                        return 0;
 305        }
 306
 307        dev_err(&dev->dev, "unable to assign resources\n");
 308        return ret;
 309}
 310
 311/**
 312 * pnp_start_dev - low-level start of the PnP device
 313 * @dev: pointer to the desired device
 314 *
 315 * assumes that resources have already been allocated
 316 */
 317int pnp_start_dev(struct pnp_dev *dev)
 318{
 319        if (!pnp_can_write(dev)) {
 320                pnp_dbg(&dev->dev, "activation not supported\n");
 321                return -EINVAL;
 322        }
 323
 324        dbg_pnp_show_resources(dev, "pnp_start_dev");
 325        if (dev->protocol->set(dev) < 0) {
 326                dev_err(&dev->dev, "activation failed\n");
 327                return -EIO;
 328        }
 329
 330        dev_info(&dev->dev, "activated\n");
 331        return 0;
 332}
 333
 334/**
 335 * pnp_stop_dev - low-level disable of the PnP device
 336 * @dev: pointer to the desired device
 337 *
 338 * does not free resources
 339 */
 340int pnp_stop_dev(struct pnp_dev *dev)
 341{
 342        if (!pnp_can_disable(dev)) {
 343                pnp_dbg(&dev->dev, "disabling not supported\n");
 344                return -EINVAL;
 345        }
 346        if (dev->protocol->disable(dev) < 0) {
 347                dev_err(&dev->dev, "disable failed\n");
 348                return -EIO;
 349        }
 350
 351        dev_info(&dev->dev, "disabled\n");
 352        return 0;
 353}
 354
 355/**
 356 * pnp_activate_dev - activates a PnP device for use
 357 * @dev: pointer to the desired device
 358 *
 359 * does not validate or set resources so be careful.
 360 */
 361int pnp_activate_dev(struct pnp_dev *dev)
 362{
 363        int error;
 364
 365        if (dev->active)
 366                return 0;
 367
 368        /* ensure resources are allocated */
 369        if (pnp_auto_config_dev(dev))
 370                return -EBUSY;
 371
 372        error = pnp_start_dev(dev);
 373        if (error)
 374                return error;
 375
 376        dev->active = 1;
 377        return 0;
 378}
 379
 380/**
 381 * pnp_disable_dev - disables device
 382 * @dev: pointer to the desired device
 383 *
 384 * inform the correct pnp protocol so that resources can be used by other devices
 385 */
 386int pnp_disable_dev(struct pnp_dev *dev)
 387{
 388        int error;
 389
 390        if (!dev->active)
 391                return 0;
 392
 393        error = pnp_stop_dev(dev);
 394        if (error)
 395                return error;
 396
 397        dev->active = 0;
 398
 399        /* release the resources so that other devices can use them */
 400        mutex_lock(&pnp_res_mutex);
 401        pnp_clean_resource_table(dev);
 402        mutex_unlock(&pnp_res_mutex);
 403
 404        return 0;
 405}
 406
 407EXPORT_SYMBOL(pnp_start_dev);
 408EXPORT_SYMBOL(pnp_stop_dev);
 409EXPORT_SYMBOL(pnp_activate_dev);
 410EXPORT_SYMBOL(pnp_disable_dev);
 411
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.