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