manager.c 9.3 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3
/*
 * manager.c - Resource Management, Conflict Resolution, Activation and Disabling of Devices
 *
4
 * based on isapnp.c resource management (c) Jaroslav Kysela <perex@perex.cz>
L
Linus Torvalds 已提交
5
 * Copyright 2003 Adam Belay <ambx1@neo.rr.com>
6 7
 * Copyright (C) 2008 Hewlett-Packard Development Company, L.P.
 *	Bjorn Helgaas <bjorn.helgaas@hp.com>
L
Linus Torvalds 已提交
8 9 10 11 12 13 14
 */

#include <linux/errno.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/pnp.h>
T
Tim Schmielau 已提交
15 16
#include <linux/slab.h>
#include <linux/bitmap.h>
D
Daniel Walker 已提交
17
#include <linux/mutex.h>
L
Linus Torvalds 已提交
18 19
#include "base.h"

D
Daniel Walker 已提交
20
DEFINE_MUTEX(pnp_res_mutex);
L
Linus Torvalds 已提交
21 22 23

static int pnp_assign_port(struct pnp_dev *dev, struct pnp_port *rule, int idx)
{
24
	struct resource *res, local_res;
L
Linus Torvalds 已提交
25

26 27
	res = pnp_get_resource(dev, IORESOURCE_IO, idx);
	if (res) {
28
		dev_dbg(&dev->dev, "  io %d already set to %#llx-%#llx "
29 30
			"flags %#lx\n", idx, (unsigned long long) res->start,
			(unsigned long long) res->end, res->flags);
31
		return 0;
32 33
	}

34 35 36 37
	res = &local_res;
	res->flags = rule->flags | IORESOURCE_AUTO;
	res->start = 0;
	res->end = 0;
L
Linus Torvalds 已提交
38 39

	if (!rule->size) {
40
		res->flags |= IORESOURCE_DISABLED;
41
		dev_dbg(&dev->dev, "  io %d disabled\n", idx);
42
		goto __add;
L
Linus Torvalds 已提交
43 44
	}

45 46
	res->start = rule->min;
	res->end = res->start + rule->size - 1;
L
Linus Torvalds 已提交
47

48
	while (!pnp_check_port(dev, res)) {
49 50 51
		res->start += rule->align;
		res->end = res->start + rule->size - 1;
		if (res->start > rule->max || !rule->align) {
52 53 54 55
			dev_dbg(&dev->dev, "  couldn't assign io %d "
				"(min %#llx max %#llx)\n", idx,
				(unsigned long long) rule->min,
				(unsigned long long) rule->max);
56
			return -EBUSY;
57
		}
L
Linus Torvalds 已提交
58
	}
59 60 61

__add:
	pnp_add_io_resource(dev, res->start, res->end, res->flags);
62
	return 0;
L
Linus Torvalds 已提交
63 64 65 66
}

static int pnp_assign_mem(struct pnp_dev *dev, struct pnp_mem *rule, int idx)
{
67
	struct resource *res, local_res;
L
Linus Torvalds 已提交
68

69 70
	res = pnp_get_resource(dev, IORESOURCE_MEM, idx);
	if (res) {
71
		dev_dbg(&dev->dev, "  mem %d already set to %#llx-%#llx "
72 73
			"flags %#lx\n", idx, (unsigned long long) res->start,
			(unsigned long long) res->end, res->flags);
74
		return 0;
75 76
	}

77 78 79 80
	res = &local_res;
	res->flags = rule->flags | IORESOURCE_AUTO;
	res->start = 0;
	res->end = 0;
L
Linus Torvalds 已提交
81 82

	if (!(rule->flags & IORESOURCE_MEM_WRITEABLE))
83
		res->flags |= IORESOURCE_READONLY;
L
Linus Torvalds 已提交
84
	if (rule->flags & IORESOURCE_MEM_CACHEABLE)
85
		res->flags |= IORESOURCE_CACHEABLE;
L
Linus Torvalds 已提交
86
	if (rule->flags & IORESOURCE_MEM_RANGELENGTH)
87
		res->flags |= IORESOURCE_RANGELENGTH;
L
Linus Torvalds 已提交
88
	if (rule->flags & IORESOURCE_MEM_SHADOWABLE)
89
		res->flags |= IORESOURCE_SHADOWABLE;
L
Linus Torvalds 已提交
90 91

	if (!rule->size) {
92
		res->flags |= IORESOURCE_DISABLED;
93
		dev_dbg(&dev->dev, "  mem %d disabled\n", idx);
94
		goto __add;
L
Linus Torvalds 已提交
95 96
	}

97 98
	res->start = rule->min;
	res->end = res->start + rule->size - 1;
L
Linus Torvalds 已提交
99

100
	while (!pnp_check_mem(dev, res)) {
101 102 103
		res->start += rule->align;
		res->end = res->start + rule->size - 1;
		if (res->start > rule->max || !rule->align) {
104 105 106 107
			dev_dbg(&dev->dev, "  couldn't assign mem %d "
				"(min %#llx max %#llx)\n", idx,
				(unsigned long long) rule->min,
				(unsigned long long) rule->max);
108
			return -EBUSY;
109
		}
L
Linus Torvalds 已提交
110
	}
111 112 113

__add:
	pnp_add_mem_resource(dev, res->start, res->end, res->flags);
114
	return 0;
L
Linus Torvalds 已提交
115 116
}

B
Bjorn Helgaas 已提交
117
static int pnp_assign_irq(struct pnp_dev *dev, struct pnp_irq *rule, int idx)
L
Linus Torvalds 已提交
118
{
119
	struct resource *res, local_res;
L
Linus Torvalds 已提交
120 121 122 123 124 125 126
	int i;

	/* IRQ priority: this table is good for i386 */
	static unsigned short xtab[16] = {
		5, 10, 11, 12, 9, 14, 15, 7, 3, 4, 13, 0, 1, 6, 8, 2
	};

127 128
	res = pnp_get_resource(dev, IORESOURCE_IRQ, idx);
	if (res) {
129
		dev_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 135 136 137
	res = &local_res;
	res->flags = rule->flags | IORESOURCE_AUTO;
	res->start = -1;
	res->end = -1;
L
Linus Torvalds 已提交
138

139
	if (bitmap_empty(rule->map.bits, PNP_IRQ_NR)) {
140
		res->flags |= IORESOURCE_DISABLED;
141
		dev_dbg(&dev->dev, "  irq %d disabled\n", idx);
142
		goto __add;
L
Linus Torvalds 已提交
143 144 145
	}

	/* TBD: need check for >16 IRQ */
146
	res->start = find_next_bit(rule->map.bits, PNP_IRQ_NR, 16);
147 148
	if (res->start < PNP_IRQ_NR) {
		res->end = res->start;
149
		goto __add;
L
Linus Torvalds 已提交
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 155
			if (pnp_check_irq(dev, res))
				goto __add;
L
Linus Torvalds 已提交
156 157
		}
	}
158 159 160 161 162 163 164 165 166

	if (rule->flags & IORESOURCE_IRQ_OPTIONAL) {
		res->start = -1;
		res->end = -1;
		res->flags |= IORESOURCE_DISABLED;
		dev_dbg(&dev->dev, "  irq %d disabled (optional)\n", idx);
		goto __add;
	}

167
	dev_dbg(&dev->dev, "  couldn't assign irq %d\n", idx);
168
	return -EBUSY;
169 170 171

__add:
	pnp_add_irq_resource(dev, res->start, res->flags);
172
	return 0;
L
Linus Torvalds 已提交
173 174
}

175
static int pnp_assign_dma(struct pnp_dev *dev, struct pnp_dma *rule, int idx)
L
Linus Torvalds 已提交
176
{
177
	struct resource *res, local_res;
L
Linus Torvalds 已提交
178 179 180 181 182 183 184
	int i;

	/* DMA priority: this table is good for i386 */
	static unsigned short xtab[8] = {
		1, 3, 5, 6, 7, 0, 2, 4
	};

185 186
	res = pnp_get_resource(dev, IORESOURCE_DMA, idx);
	if (res) {
187
		dev_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 193 194 195
	res = &local_res;
	res->flags = rule->flags | IORESOURCE_AUTO;
	res->start = -1;
	res->end = -1;
L
Linus Torvalds 已提交
196 197

	for (i = 0; i < 8; i++) {
B
Bjorn Helgaas 已提交
198
		if (rule->map & (1 << xtab[i])) {
199
			res->start = res->end = xtab[i];
200 201
			if (pnp_check_dma(dev, res))
				goto __add;
L
Linus Torvalds 已提交
202 203
		}
	}
204
#ifdef MAX_DMA_CHANNELS
205
	res->start = res->end = MAX_DMA_CHANNELS;
206
#endif
207
	res->flags |= IORESOURCE_DISABLED;
208
	dev_dbg(&dev->dev, "  disable dma %d\n", idx);
L
Linus Torvalds 已提交
209

210 211
__add:
	pnp_add_dma_resource(dev, res->start, res->flags);
212
	return 0;
213 214
}

215
void pnp_init_resources(struct pnp_dev *dev)
L
Linus Torvalds 已提交
216
{
217
	pnp_free_resources(dev);
L
Linus Torvalds 已提交
218 219
}

220
static void pnp_clean_resource_table(struct pnp_dev *dev)
L
Linus Torvalds 已提交
221
{
222 223 224 225 226
	struct pnp_resource *pnp_res, *tmp;

	list_for_each_entry_safe(pnp_res, tmp, &dev->resources, list) {
		if (pnp_res->res.flags & IORESOURCE_AUTO)
			pnp_free_resource(pnp_res);
L
Linus Torvalds 已提交
227 228 229 230 231 232
	}
}

/**
 * pnp_assign_resources - assigns resources to the device based on the specified dependent number
 * @dev: pointer to the desired device
233
 * @set: the dependent function number
L
Linus Torvalds 已提交
234
 */
235
static int pnp_assign_resources(struct pnp_dev *dev, int set)
L
Linus Torvalds 已提交
236
{
237
	struct pnp_option *option;
L
Linus Torvalds 已提交
238
	int nport = 0, nmem = 0, nirq = 0, ndma = 0;
239
	int ret = 0;
L
Linus Torvalds 已提交
240

241
	dev_dbg(&dev->dev, "pnp_assign_resources, try dependent set %d\n", set);
D
Daniel Walker 已提交
242
	mutex_lock(&pnp_res_mutex);
243
	pnp_clean_resource_table(dev);
L
Linus Torvalds 已提交
244

245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
	list_for_each_entry(option, &dev->options, list) {
		if (pnp_option_is_dependent(option) &&
		    pnp_option_set(option) != set)
				continue;

		switch (option->type) {
		case IORESOURCE_IO:
			ret = pnp_assign_port(dev, &option->u.port, nport++);
			break;
		case IORESOURCE_MEM:
			ret = pnp_assign_mem(dev, &option->u.mem, nmem++);
			break;
		case IORESOURCE_IRQ:
			ret = pnp_assign_irq(dev, &option->u.irq, nirq++);
			break;
		case IORESOURCE_DMA:
			ret = pnp_assign_dma(dev, &option->u.dma, ndma++);
			break;
		default:
			ret = -EINVAL;
			break;
L
Linus Torvalds 已提交
266
		}
267 268 269
		if (ret < 0)
			break;
	}
L
Linus Torvalds 已提交
270

D
Daniel Walker 已提交
271
	mutex_unlock(&pnp_res_mutex);
272 273 274 275 276 277
	if (ret < 0) {
		dev_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;
L
Linus Torvalds 已提交
278 279 280 281 282 283 284 285
}

/**
 * pnp_auto_config_dev - automatically assigns resources to a device
 * @dev: pointer to the desired device
 */
int pnp_auto_config_dev(struct pnp_dev *dev)
{
286
	int i, ret;
L
Linus Torvalds 已提交
287

B
Bjorn Helgaas 已提交
288
	if (!pnp_can_configure(dev)) {
289
		dev_dbg(&dev->dev, "configuration not supported\n");
L
Linus Torvalds 已提交
290 291 292
		return -ENODEV;
	}

293 294 295 296 297 298 299
	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)
L
Linus Torvalds 已提交
300 301 302
			return 0;
	}

303
	dev_err(&dev->dev, "unable to assign resources\n");
304
	return ret;
L
Linus Torvalds 已提交
305 306
}

307 308 309 310
/**
 * pnp_start_dev - low-level start of the PnP device
 * @dev: pointer to the desired device
 *
B
Bjorn Helgaas 已提交
311
 * assumes that resources have already been allocated
312 313 314 315
 */
int pnp_start_dev(struct pnp_dev *dev)
{
	if (!pnp_can_write(dev)) {
316
		dev_dbg(&dev->dev, "activation not supported\n");
317 318 319
		return -EINVAL;
	}

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 324 325
		return -EIO;
	}

326
	dev_info(&dev->dev, "activated\n");
327 328 329 330 331 332 333 334 335 336 337 338
	return 0;
}

/**
 * pnp_stop_dev - low-level disable of the PnP device
 * @dev: pointer to the desired device
 *
 * does not free resources
 */
int pnp_stop_dev(struct pnp_dev *dev)
{
	if (!pnp_can_disable(dev)) {
339
		dev_dbg(&dev->dev, "disabling not supported\n");
340 341
		return -EINVAL;
	}
B
Bjorn Helgaas 已提交
342
	if (dev->protocol->disable(dev) < 0) {
343
		dev_err(&dev->dev, "disable failed\n");
344 345 346
		return -EIO;
	}

347
	dev_info(&dev->dev, "disabled\n");
348 349 350
	return 0;
}

L
Linus Torvalds 已提交
351 352 353 354 355 356 357 358
/**
 * pnp_activate_dev - activates a PnP device for use
 * @dev: pointer to the desired device
 *
 * does not validate or set resources so be careful.
 */
int pnp_activate_dev(struct pnp_dev *dev)
{
359 360
	int error;

B
Bjorn Helgaas 已提交
361
	if (dev->active)
362
		return 0;
L
Linus Torvalds 已提交
363 364 365 366 367

	/* ensure resources are allocated */
	if (pnp_auto_config_dev(dev))
		return -EBUSY;

368 369 370
	error = pnp_start_dev(dev);
	if (error)
		return error;
L
Linus Torvalds 已提交
371 372

	dev->active = 1;
373
	return 0;
L
Linus Torvalds 已提交
374 375 376 377 378 379 380 381 382 383
}

/**
 * pnp_disable_dev - disables device
 * @dev: pointer to the desired device
 *
 * inform the correct pnp protocol so that resources can be used by other devices
 */
int pnp_disable_dev(struct pnp_dev *dev)
{
384 385
	int error;

B
Bjorn Helgaas 已提交
386
	if (!dev->active)
387
		return 0;
L
Linus Torvalds 已提交
388

389 390 391
	error = pnp_stop_dev(dev);
	if (error)
		return error;
L
Linus Torvalds 已提交
392 393 394 395

	dev->active = 0;

	/* release the resources so that other devices can use them */
D
Daniel Walker 已提交
396
	mutex_lock(&pnp_res_mutex);
397
	pnp_clean_resource_table(dev);
D
Daniel Walker 已提交
398
	mutex_unlock(&pnp_res_mutex);
L
Linus Torvalds 已提交
399

400
	return 0;
L
Linus Torvalds 已提交
401 402
}

403 404
EXPORT_SYMBOL(pnp_start_dev);
EXPORT_SYMBOL(pnp_stop_dev);
L
Linus Torvalds 已提交
405 406
EXPORT_SYMBOL(pnp_activate_dev);
EXPORT_SYMBOL(pnp_disable_dev);