core.c 8.5 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5
/*
 * pnpacpi -- PnP ACPI driver
 *
 * Copyright (c) 2004 Matthieu Castet <castet.matthieu@free.fr>
 * Copyright (c) 2004 Li Shaohua <shaohua.li@intel.com>
6
 *
L
Linus Torvalds 已提交
7 8 9 10 11 12 13 14 15 16 17 18 19 20
 * 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
 */
21

L
Linus Torvalds 已提交
22 23
#include <linux/acpi.h>
#include <linux/pnp.h>
24
#include <linux/mod_devicetable.h>
L
Linus Torvalds 已提交
25
#include <acpi/acpi_bus.h>
26 27
#include <acpi/actypes.h>

L
Linus Torvalds 已提交
28 29 30 31
#include "pnpacpi.h"

static int num = 0;

32 33 34 35 36 37 38
/* 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.
 * Finaly only devices that have a CRS method need to be in this list.
 */
B
Bjorn Helgaas 已提交
39 40 41 42 43
static __initdata struct acpi_device_id excluded_id_list[] = {
	{"PNP0C09", 0},		/* EC */
	{"PNP0C0F", 0},		/* Link device */
	{"PNP0000", 0},		/* PIC */
	{"PNP0100", 0},		/* Timer */
44 45 46
	{"", 0},
};

L
Linus Torvalds 已提交
47 48
static inline int is_exclusive_device(struct acpi_device *dev)
{
49
	return (!acpi_match_device_ids(dev, excluded_id_list));
L
Linus Torvalds 已提交
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
}

/*
 * Compatible Device IDs
 */
#define TEST_HEX(c) \
	if (!(('0' <= (c) && (c) <= '9') || ('A' <= (c) && (c) <= 'F'))) \
		return 0
#define TEST_ALPHA(c) \
	if (!('@' <= (c) || (c) <= 'Z')) \
		return 0
static int __init ispnpidacpi(char *id)
{
	TEST_ALPHA(id[0]);
	TEST_ALPHA(id[1]);
	TEST_ALPHA(id[2]);
	TEST_HEX(id[3]);
	TEST_HEX(id[4]);
	TEST_HEX(id[5]);
	TEST_HEX(id[6]);
	if (id[7] != '\0')
		return 0;
	return 1;
}

static void __init pnpidacpi_to_pnpid(char *id, char *str)
{
	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';
}

B
Bjorn Helgaas 已提交
87 88
static int pnpacpi_get_resources(struct pnp_dev *dev,
				 struct pnp_resource_table *res)
L
Linus Torvalds 已提交
89 90
{
	acpi_status status;
B
Bjorn Helgaas 已提交
91 92
	status = pnpacpi_parse_allocated_resource((acpi_handle) dev->data,
						  &dev->res);
L
Linus Torvalds 已提交
93 94 95
	return ACPI_FAILURE(status) ? -ENODEV : 0;
}

B
Bjorn Helgaas 已提交
96 97
static int pnpacpi_set_resources(struct pnp_dev *dev,
				 struct pnp_resource_table *res)
L
Linus Torvalds 已提交
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
{
	acpi_handle handle = dev->data;
	struct acpi_buffer buffer;
	int ret = 0;
	acpi_status status;

	ret = pnpacpi_build_resource_template(handle, &buffer);
	if (ret)
		return ret;
	ret = pnpacpi_encode_resources(res, &buffer);
	if (ret) {
		kfree(buffer.pointer);
		return ret;
	}
	status = acpi_set_current_resources(handle, &buffer);
	if (ACPI_FAILURE(status))
		ret = -EINVAL;
	kfree(buffer.pointer);
	return ret;
}

static int pnpacpi_disable_resources(struct pnp_dev *dev)
{
	acpi_status status;
122

L
Linus Torvalds 已提交
123
	/* acpi_unregister_gsi(pnp_irq(dev, 0)); */
B
Bjorn Helgaas 已提交
124 125
	status = acpi_evaluate_object((acpi_handle) dev->data,
				      "_DIS", NULL, NULL);
L
Linus Torvalds 已提交
126 127 128
	return ACPI_FAILURE(status) ? -ENODEV : 0;
}

129 130
static int pnpacpi_suspend(struct pnp_dev *dev, pm_message_t state)
{
B
Bjorn Helgaas 已提交
131 132 133 134 135
	return acpi_bus_set_power((acpi_handle) dev->data,
				  acpi_pm_device_sleep_state(&dev->dev,
							     device_may_wakeup
							     (&dev->dev),
							     NULL));
136 137 138 139
}

static int pnpacpi_resume(struct pnp_dev *dev)
{
B
Bjorn Helgaas 已提交
140
	return acpi_bus_set_power((acpi_handle) dev->data, ACPI_STATE_D0);
141 142
}

A
Adrian Bunk 已提交
143
static struct pnp_protocol pnpacpi_protocol = {
B
Bjorn Helgaas 已提交
144 145 146
	.name = "Plug and Play ACPI",
	.get = pnpacpi_get_resources,
	.set = pnpacpi_set_resources,
L
Linus Torvalds 已提交
147
	.disable = pnpacpi_disable_resources,
148 149
	.suspend = pnpacpi_suspend,
	.resume = pnpacpi_resume,
L
Linus Torvalds 已提交
150 151 152 153 154 155 156 157 158
};

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;

159 160
	status = acpi_get_handle(device->handle, "_CRS", &temp);
	if (ACPI_FAILURE(status) || !ispnpidacpi(acpi_device_hid(device)) ||
B
Bjorn Helgaas 已提交
161
	    is_exclusive_device(device))
L
Linus Torvalds 已提交
162 163 164
		return 0;

	pnp_dbg("ACPI device : hid %s", acpi_device_hid(device));
B
Bjorn Helgaas 已提交
165
	dev = kzalloc(sizeof(struct pnp_dev), GFP_KERNEL);
L
Linus Torvalds 已提交
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
	if (!dev) {
		pnp_err("Out of memory");
		return -ENOMEM;
	}
	dev->data = device->handle;
	/* .enabled means if the device can decode the resources */
	dev->active = device->status.enabled;
	status = acpi_get_handle(device->handle, "_SRS", &temp);
	if (ACPI_SUCCESS(status))
		dev->capabilities |= PNP_CONFIGURABLE;
	dev->capabilities |= PNP_READ;
	if (device->flags.dynamic_status)
		dev->capabilities |= PNP_WRITE;
	if (device->flags.removable)
		dev->capabilities |= PNP_REMOVABLE;
	status = acpi_get_handle(device->handle, "_DIS", &temp);
	if (ACPI_SUCCESS(status))
		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;
193

L
Linus Torvalds 已提交
194
	/* set the initial values for the PnP device */
195
	dev_id = kzalloc(sizeof(struct pnp_id), GFP_KERNEL);
L
Linus Torvalds 已提交
196 197 198 199 200
	if (!dev_id)
		goto err;
	pnpidacpi_to_pnpid(acpi_device_hid(device), dev_id->id);
	pnp_add_id(dev_id, dev);

B
Bjorn Helgaas 已提交
201
	if (dev->active) {
L
Linus Torvalds 已提交
202
		/* parse allocated resource */
B
Bjorn Helgaas 已提交
203 204
		status =
		    pnpacpi_parse_allocated_resource(device->handle, &dev->res);
L
Linus Torvalds 已提交
205
		if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) {
B
Bjorn Helgaas 已提交
206 207
			pnp_err("PnPACPI: METHOD_NAME__CRS failure for %s",
				dev_id->id);
L
Linus Torvalds 已提交
208 209 210 211
			goto err1;
		}
	}

B
Bjorn Helgaas 已提交
212
	if (dev->capabilities & PNP_CONFIGURABLE) {
213
		status = pnpacpi_parse_resource_option_data(device->handle,
B
Bjorn Helgaas 已提交
214
							    dev);
L
Linus Torvalds 已提交
215
		if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) {
B
Bjorn Helgaas 已提交
216 217
			pnp_err("PnPACPI: METHOD_NAME__PRS failure for %s",
				dev_id->id);
L
Linus Torvalds 已提交
218 219 220
			goto err1;
		}
	}
221

L
Linus Torvalds 已提交
222 223 224 225 226 227 228 229
	/* parse compatible ids */
	if (device->flags.compatible_ids) {
		struct acpi_compatible_id_list *cid_list = device->pnp.cid_list;
		int i;

		for (i = 0; i < cid_list->count; i++) {
			if (!ispnpidacpi(cid_list->id[i].value))
				continue;
230
			dev_id = kzalloc(sizeof(struct pnp_id), GFP_KERNEL);
L
Linus Torvalds 已提交
231 232 233 234 235 236 237 238 239 240 241 242
			if (!dev_id)
				continue;

			pnpidacpi_to_pnpid(cid_list->id[i].value, dev_id->id);
			pnp_add_id(dev_id, dev);
		}
	}

	/* clear out the damaged flags */
	if (!dev->active)
		pnp_init_resource_table(&dev->res);
	pnp_add_device(dev);
B
Bjorn Helgaas 已提交
243
	num++;
L
Linus Torvalds 已提交
244 245

	return AE_OK;
B
Bjorn Helgaas 已提交
246
      err1:
L
Linus Torvalds 已提交
247
	kfree(dev_id);
B
Bjorn Helgaas 已提交
248
      err:
L
Linus Torvalds 已提交
249 250 251 252 253
	kfree(dev);
	return -EINVAL;
}

static acpi_status __init pnpacpi_add_device_handler(acpi_handle handle,
B
Bjorn Helgaas 已提交
254 255
						     u32 lvl, void *context,
						     void **rv)
L
Linus Torvalds 已提交
256 257 258 259 260 261 262 263 264 265
{
	struct acpi_device *device;

	if (!acpi_bus_get_device(handle, &device))
		pnpacpi_add_device(device);
	else
		return AE_CTRL_DEPTH;
	return AE_OK;
}

266 267
static int __init acpi_pnp_match(struct device *dev, void *_pnp)
{
B
Bjorn Helgaas 已提交
268 269
	struct acpi_device *acpi = to_acpi_device(dev);
	struct pnp_dev *pnp = _pnp;
270 271 272

	/* true means it matched */
	return acpi->flags.hardware_id
B
Bjorn Helgaas 已提交
273 274
	    && !acpi_get_physical_device(acpi->handle)
	    && compare_pnp_id(pnp->id, acpi->pnp.hardware_id);
275 276
}

B
Bjorn Helgaas 已提交
277
static int __init acpi_pnp_find_device(struct device *dev, acpi_handle * handle)
278
{
B
Bjorn Helgaas 已提交
279 280
	struct device *adev;
	struct acpi_device *acpi;
281 282

	adev = bus_find_device(&acpi_bus_type, NULL,
B
Bjorn Helgaas 已提交
283
			       to_pnp_dev(dev), acpi_pnp_match);
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
	if (!adev)
		return -ENODEV;

	acpi = to_acpi_device(adev);
	*handle = acpi->handle;
	put_device(adev);
	return 0;
}

/* complete initialization of a PNPACPI device includes having
 * pnpdev->dev.archdata.acpi_handle point to its ACPI sibling.
 */
static struct acpi_bus_type __initdata acpi_pnp_bus = {
	.bus = &pnp_bus_type,
	.find_device = acpi_pnp_find_device,
};

L
Linus Torvalds 已提交
301
int pnpacpi_disabled __initdata;
A
Adrian Bunk 已提交
302
static int __init pnpacpi_init(void)
L
Linus Torvalds 已提交
303 304 305 306 307 308 309
{
	if (acpi_disabled || pnpacpi_disabled) {
		pnp_info("PnP ACPI: disabled");
		return 0;
	}
	pnp_info("PnP ACPI init");
	pnp_register_protocol(&pnpacpi_protocol);
310
	register_acpi_bus_type(&acpi_pnp_bus);
L
Linus Torvalds 已提交
311 312
	acpi_get_devices(NULL, pnpacpi_add_device_handler, NULL, NULL);
	pnp_info("PnP ACPI: found %d devices", num);
313
	unregister_acpi_bus_type(&acpi_pnp_bus);
314
	pnp_platform_devices = 1;
L
Linus Torvalds 已提交
315 316
	return 0;
}
B
Bjorn Helgaas 已提交
317

L
Linus Torvalds 已提交
318 319 320 321 322 323 324 325 326 327
subsys_initcall(pnpacpi_init);

static int __init pnpacpi_setup(char *str)
{
	if (str == NULL)
		return 1;
	if (!strncmp(str, "off", 3))
		pnpacpi_disabled = 1;
	return 1;
}
B
Bjorn Helgaas 已提交
328

L
Linus Torvalds 已提交
329 330
__setup("pnpacpi=", pnpacpi_setup);

A
Adrian Bunk 已提交
331
#if 0
L
Linus Torvalds 已提交
332
EXPORT_SYMBOL(pnpacpi_protocol);
A
Adrian Bunk 已提交
333
#endif