From 2e0a186ba09a99620778723cdb19359416ca7a2d Mon Sep 17 00:00:00 2001 From: "Sergey Semyonov (Serge)" Date: Sun, 26 Sep 2010 07:30:13 +0000 Subject: [PATCH] scan acpi bus and create device tree git-svn-id: svn://kolibrios.org@1625 a494cfbc-eb01-0410-851d-a64ba20cac60 --- drivers/devman/Makefile | 5 +- drivers/devman/acpi.c | 1237 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 1190 insertions(+), 52 deletions(-) diff --git a/drivers/devman/Makefile b/drivers/devman/Makefile index b8ad33fdff..d49b95313a 100644 --- a/drivers/devman/Makefile +++ b/drivers/devman/Makefile @@ -19,7 +19,7 @@ LDFLAGS = -nostdlib -shared -s -Map acpi.map --image-base 0\ LIBPATH:= -L$(DRV_DIR)/ddk -L./acpica -LIBS:= -lacpica -lgcc -ldrv -lcore +LIBS:= -lacpica -lgcc -lddk -lcore NAME= acpi @@ -34,7 +34,8 @@ NAME_OBJS = $(patsubst %.S, %.o, $(patsubst %.asm, %.o,\ $(NAME).dll: $(NAME_OBJS) acpi.lds Makefile - ld $(LIBPATH) $(LDFLAGS) -T acpi.lds -o $@ $(NAME_OBJS) $(LIBS) + ld $(LIBPATH) $(LDFLAGS) -T acpi.lds -o $@ $(NAME_OBJS) $(LIBS) + kpack $(NAME).dll %.o : %.c $(HFILES) Makefile $(CC) $(CFLAGS) $(DEFINES) $(INCLUDES) -o $@ $< diff --git a/drivers/devman/acpi.c b/drivers/devman/acpi.c index 323a419209..3b5837ae7e 100644 --- a/drivers/devman/acpi.c +++ b/drivers/devman/acpi.c @@ -1,9 +1,31 @@ -#include +#include +#include +#include +#include #include #include "acpi.h" +#define ACPI_BUS_CLASS "system_bus" +#define ACPI_BUS_HID "KLBSYBUS" +#define ACPI_BUS_DEVICE_NAME "System Bus" + + +#define ACPI_IS_ROOT_DEVICE(device) (!(device)->parent) + +static LIST_HEAD(acpi_device_list); +static LIST_HEAD(acpi_bus_id_list); +DEFINE_MUTEX(acpi_device_lock); + +struct acpi_device_bus_id +{ + char bus_id[15]; + unsigned int instance_no; + struct list_head node; +}; + + #define ACPI_NS_ROOT_PATH "\\" #define ACPI_NS_SYSTEM_BUS "_SB_" @@ -15,6 +37,55 @@ enum acpi_irq_model_id { ACPI_IRQ_MODEL_COUNT }; +enum acpi_bus_removal_type { + ACPI_BUS_REMOVAL_NORMAL = 0, + ACPI_BUS_REMOVAL_EJECT, + ACPI_BUS_REMOVAL_SUPRISE, + ACPI_BUS_REMOVAL_TYPE_COUNT +}; + +enum acpi_bus_device_type { + ACPI_BUS_TYPE_DEVICE = 0, + ACPI_BUS_TYPE_POWER, + ACPI_BUS_TYPE_PROCESSOR, + ACPI_BUS_TYPE_THERMAL, + ACPI_BUS_TYPE_POWER_BUTTON, + ACPI_BUS_TYPE_SLEEP_BUTTON, + ACPI_BUS_DEVICE_TYPE_COUNT +}; + +/* + * _HID definitions + * HIDs must conform to ACPI spec(6.1.4) + * KolibriOS specific HIDs do not apply to this and begin with KOS: + */ + +#define ACPI_POWER_HID "KLBPOWER" +#define ACPI_PROCESSOR_OBJECT_HID "KLBCPU" +#define ACPI_SYSTEM_HID "KLBSYSTM" +#define ACPI_THERMAL_HID "KLBTHERM" +#define ACPI_BUTTON_HID_POWERF "KLBPWRBN" +#define ACPI_BUTTON_HID_SLEEPF "KLBSLPBN" +#define ACPI_VIDEO_HID "KLBVIDEO" +#define ACPI_BAY_HID "KLBIOBAY" +#define ACPI_DOCK_HID "KLBDOCK" +/* Quirk for broken IBM BIOSes */ +#define ACPI_SMBUS_IBM_HID "SMBUSIBM" + + +#define STRUCT_TO_INT(s) (*((int*)&s)) + +#define ACPI_STA_DEFAULT (ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED | \ + ACPI_STA_DEVICE_UI | ACPI_STA_DEVICE_FUNCTIONING) + +#define PCI_MAX_DEVICES 32 +#define PCI_MAX_PINS 4 + +#define IRQ_TABLE_ENTRIES (PCI_MAX_DEVICES * PCI_MAX_PINS) + +static int irqtable[IRQ_TABLE_ENTRIES]; +static ACPI_HANDLE pci_root_handle; + #define addr_offset(addr, off) \ (addr_t)((addr_t)(addr) + (addr_t)(off)) @@ -24,77 +95,851 @@ enum acpi_irq_model_id { #define acpi_remap( addr ) MapIoMem((void*)(addr),4096, 0x01) -ACPI_STATUS -get_device_by_hid_callback(ACPI_HANDLE obj, u32_t depth, void* context, - void** retval) + +struct acpi_bus_ops { - static u32_t counter = 0; - static char buff[256]; + u32_t acpi_op_add:1; + u32_t acpi_op_start:1; +}; - ACPI_STATUS status; +struct acpi_device_flags { + u32 dynamic_status:1; + u32 bus_address:1; + u32 removable:1; + u32 ejectable:1; + u32 lockable:1; + u32 suprise_removal_ok:1; + u32 power_manageable:1; + u32 performance_manageable:1; + u32 wake_capable:1; /* Wakeup(_PRW) supported? */ + u32 force_power_state:1; + u32 reserved:22; +}; - ACPI_BUFFER buffer; +struct acpi_device_status { + u32 present:1; + u32 enabled:1; + u32 show_in_ui:1; + u32 functional:1; + u32 battery_present:1; + u32 reserved:27; +}; - ACPI_DEVICE_INFO *info; - // *retval = NULL; +typedef char acpi_bus_id[8]; +typedef unsigned long acpi_bus_address; +typedef char acpi_device_name[40]; +typedef char acpi_device_class[20]; - buffer.Length = 255; - buffer.Pointer = buff; +struct acpi_hardware_id { + struct list_head list; + char *id; +}; - status = AcpiGetName(obj, ACPI_FULL_PATHNAME, &buffer); - if (status != AE_OK) { - return AE_CTRL_TERMINATE; +struct acpi_device_pnp +{ + acpi_bus_id bus_id; /* Object name */ + acpi_bus_address bus_address; /* _ADR */ + char *unique_id; /* _UID */ + struct list_head ids; /* _HID and _CIDs */ + acpi_device_name device_name; /* Driver-determined */ + acpi_device_class device_class; /* " */ +}; + + +struct acpi_device +{ + int device_type; + ACPI_HANDLE handle; /* no handle for fixed hardware */ + struct acpi_device *parent; + struct list_head children; + struct list_head node; +// struct list_head wakeup_list; + struct acpi_device_status status; + struct acpi_device_flags flags; + struct acpi_device_pnp pnp; +// struct acpi_device_power power; +// struct acpi_device_wakeup wakeup; +// struct acpi_device_perf performance; +// struct acpi_device_dir dir; +// struct acpi_device_ops ops; +// struct acpi_driver *driver; + void *driver_data; +// struct device dev; + struct acpi_bus_ops bus_ops; /* workaround for different code path for hotplug */ + // enum acpi_bus_removal_type removal_type; /* indicate for different removal type */ +}; + +struct acpi_device *acpi_root; + + +static void +acpi_util_eval_error(ACPI_HANDLE h, ACPI_STRING p, ACPI_STATUS s) +{ +#ifdef ACPI_DEBUG_OUTPUT + char prefix[80] = {'\0'}; + ACPI_BUFFER buffer = {sizeof(prefix), prefix}; + AcpiGetName(h, ACPI_FULL_PATHNAME, &buffer); + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Evaluate [%s.%s]: %s\n", + (char *) prefix, p, AcpiFormatException(s))); +#else + return; +#endif +} + +ACPI_STATUS +acpi_evaluate_integer(ACPI_HANDLE handle, ACPI_STRING pathname, + ACPI_OBJECT_LIST *arguments, unsigned long long *data) +{ + ACPI_STATUS status = AE_OK; + ACPI_OBJECT element; + ACPI_BUFFER buffer = { 0, NULL }; + + if (!data) + return AE_BAD_PARAMETER; + + buffer.Length = sizeof(ACPI_OBJECT); + buffer.Pointer = &element; + status = AcpiEvaluateObject(handle, pathname, arguments, &buffer); + if (ACPI_FAILURE(status)) { + acpi_util_eval_error(handle, pathname, status); + return status; } - buff[buffer.Length] = '\0'; + if (element.Type != ACPI_TYPE_INTEGER) { + acpi_util_eval_error(handle, pathname, AE_BAD_DATA); + return AE_BAD_DATA; + } - dbgprintf("device %d %s ", counter, buff); + *data = element.Integer.Value; - status = AcpiGetObjectInfo(obj, &info); + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Return value [%llu]\n", *data)); - if (ACPI_SUCCESS (status)) + return AE_OK; +} + +void acpi_bus_data_handler(ACPI_HANDLE handle, void *context) +{ + + /* TBD */ + + return; +} + + +int acpi_bus_get_device(ACPI_HANDLE handle, struct acpi_device **device) +{ + ACPI_STATUS status = AE_OK; + + if (!device) { - if (info->Valid & ACPI_VALID_HID) - dbgprintf (" HID: %s", info->HardwareId.String); - + return -EINVAL; }; - dbgprintf("\n"); - counter++; + /* TBD: Support fixed-feature devices */ - return AE_OK; + status = AcpiGetData(handle, acpi_bus_data_handler, (void **)device); + if (ACPI_FAILURE(status) || !*device) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No context for object [%p]\n", + handle)); + return -ENODEV; + } + return 0; } -prt_walk_table(ACPI_BUFFER *prt) + +ACPI_STATUS acpi_bus_get_status_handle(ACPI_HANDLE handle, + unsigned long long *sta) { - ACPI_PCI_ROUTING_TABLE *entry; - char *prtptr; + ACPI_STATUS status; - /* First check to see if there is a table to walk. */ - if (prt == NULL || prt->Pointer == NULL) - return; - - /* Walk the table executing the handler function for each entry. */ - prtptr = prt->Pointer; - entry = (ACPI_PCI_ROUTING_TABLE *)prtptr; - while (entry->Length != 0) + status = acpi_evaluate_integer(handle, "_STA", NULL, sta); + if (ACPI_SUCCESS(status)) { + return AE_OK; + }; - dbgprintf("adress: %x %x ", (u32_t)(entry->Address>>32), - (u32_t)entry->Address); - dbgprintf("pin: %d index: %d source: %s\n", - entry->Pin, - entry->SourceIndex, - entry->Source); + if (status == AE_NOT_FOUND) + { + *sta = ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED | + ACPI_STA_DEVICE_UI | ACPI_STA_DEVICE_FUNCTIONING; + return AE_OK; + } + return status; +} -// handler(entry, arg); - prtptr += entry->Length; - entry = (ACPI_PCI_ROUTING_TABLE *)prtptr; + + +static int acpi_bus_type_and_status(ACPI_HANDLE handle, int *type, + unsigned long long *sta) +{ + ACPI_STATUS status; + ACPI_OBJECT_TYPE acpi_type; + + status = AcpiGetType(handle, &acpi_type); + if (ACPI_FAILURE(status)) + { + return -ENODEV; + }; + + switch (acpi_type) + { + case ACPI_TYPE_ANY: /* for ACPI_ROOT_OBJECT */ + case ACPI_TYPE_DEVICE: + *type = ACPI_BUS_TYPE_DEVICE; + status = acpi_bus_get_status_handle(handle, sta); + if (ACPI_FAILURE(status)) + { + return -ENODEV; + }; + break; + + case ACPI_TYPE_PROCESSOR: + *type = ACPI_BUS_TYPE_PROCESSOR; + status = acpi_bus_get_status_handle(handle, sta); + if (ACPI_FAILURE(status)) + { + return -ENODEV; + }; + break; + case ACPI_TYPE_THERMAL: + *type = ACPI_BUS_TYPE_THERMAL; + *sta = ACPI_STA_DEFAULT; + break; + case ACPI_TYPE_POWER: + *type = ACPI_BUS_TYPE_POWER; + *sta = ACPI_STA_DEFAULT; + break; + default: + return -ENODEV; + } + + return 0; +} + +static struct acpi_device *acpi_bus_get_parent(ACPI_HANDLE handle) +{ + ACPI_STATUS status; + struct acpi_device *device; + int ret; + + /* + * Fixed hardware devices do not appear in the namespace and do not + * have handles, but we fabricate acpi_devices for them, so we have + * to deal with them specially. + */ + if (handle == NULL) + { + return acpi_root; + }; + + do + { + status = AcpiGetParent(handle, &handle); + if (status == AE_NULL_ENTRY) + { + return NULL; + }; + if (ACPI_FAILURE(status)) + { + return acpi_root; + }; + + ret = acpi_bus_get_device(handle, &device); + if (ret == 0) + { + return device; + }; + } while (1); +} + + +static void acpi_device_get_busid(struct acpi_device *device) +{ + char bus_id[5] = { '?', 0 }; + struct acpi_buffer buffer = { sizeof(bus_id), bus_id }; + int i = 0; + + /* + * Bus ID + * ------ + * The device's Bus ID is simply the object name. + * TBD: Shouldn't this value be unique (within the ACPI namespace)? + */ + if (ACPI_IS_ROOT_DEVICE(device)) { + strcpy(device->pnp.bus_id, "ACPI"); + return; + } + + switch (device->device_type) + { + case ACPI_BUS_TYPE_POWER_BUTTON: + strcpy(device->pnp.bus_id, "PWRF"); + break; + case ACPI_BUS_TYPE_SLEEP_BUTTON: + strcpy(device->pnp.bus_id, "SLPF"); + break; + default: + AcpiGetName(device->handle, ACPI_SINGLE_NAME, &buffer); + /* Clean up trailing underscores (if any) */ + for (i = 3; i > 1; i--) + { + if (bus_id[i] == '_') + bus_id[i] = '\0'; + else + break; + } + strcpy(device->pnp.bus_id, bus_id); + break; } } +static int acpi_bus_get_flags(struct acpi_device *device) +{ + ACPI_STATUS status = AE_OK; + ACPI_HANDLE temp = NULL; + + /* Presence of _STA indicates 'dynamic_status' */ + status = AcpiGetHandle(device->handle, "_STA", &temp); + if (ACPI_SUCCESS(status)) + device->flags.dynamic_status = 1; + + /* Presence of _RMV indicates 'removable' */ + status = AcpiGetHandle(device->handle, "_RMV", &temp); + if (ACPI_SUCCESS(status)) + device->flags.removable = 1; + + /* Presence of _EJD|_EJ0 indicates 'ejectable' */ + status = AcpiGetHandle(device->handle, "_EJD", &temp); + if (ACPI_SUCCESS(status)) + device->flags.ejectable = 1; + else { + status = AcpiGetHandle(device->handle, "_EJ0", &temp); + if (ACPI_SUCCESS(status)) + device->flags.ejectable = 1; + } + + /* Presence of _LCK indicates 'lockable' */ + status = AcpiGetHandle(device->handle, "_LCK", &temp); + if (ACPI_SUCCESS(status)) + device->flags.lockable = 1; + + /* Presence of _PS0|_PR0 indicates 'power manageable' */ + status = AcpiGetHandle(device->handle, "_PS0", &temp); + if (ACPI_FAILURE(status)) + status = AcpiGetHandle(device->handle, "_PR0", &temp); + if (ACPI_SUCCESS(status)) + device->flags.power_manageable = 1; + + /* Presence of _PRW indicates wake capable */ + status = AcpiGetHandle(device->handle, "_PRW", &temp); + if (ACPI_SUCCESS(status)) + device->flags.wake_capable = 1; + + /* TBD: Performance management */ + + return 0; +} + +/* + * acpi_bay_match - see if a device is an ejectable driver bay + * + * If an acpi object is ejectable and has one of the ACPI ATA methods defined, + * then we can safely call it an ejectable drive bay + */ +static int acpi_bay_match(struct acpi_device *device){ + ACPI_STATUS status; + ACPI_HANDLE handle; + ACPI_HANDLE tmp; + ACPI_HANDLE phandle; + + handle = device->handle; + + status = AcpiGetHandle(handle, "_EJ0", &tmp); + if (ACPI_FAILURE(status)) + return -ENODEV; + + if ((ACPI_SUCCESS(AcpiGetHandle(handle, "_GTF", &tmp))) || + (ACPI_SUCCESS(AcpiGetHandle(handle, "_GTM", &tmp))) || + (ACPI_SUCCESS(AcpiGetHandle(handle, "_STM", &tmp))) || + (ACPI_SUCCESS(AcpiGetHandle(handle, "_SDD", &tmp)))) + return 0; + + if (AcpiGetParent(handle, &phandle)) + return -ENODEV; + + if ((ACPI_SUCCESS(AcpiGetHandle(phandle, "_GTF", &tmp))) || + (ACPI_SUCCESS(AcpiGetHandle(phandle, "_GTM", &tmp))) || + (ACPI_SUCCESS(AcpiGetHandle(phandle, "_STM", &tmp))) || + (ACPI_SUCCESS(AcpiGetHandle(phandle, "_SDD", &tmp)))) + return 0; + + return -ENODEV; +} + +/* + * acpi_dock_match - see if a device has a _DCK method + */ +static int acpi_dock_match(struct acpi_device *device) +{ + ACPI_HANDLE tmp; + return AcpiGetHandle(device->handle, "_DCK", &tmp); +} + +char *acpi_device_hid(struct acpi_device *device) +{ + struct acpi_hardware_id *hid; + + hid = list_first_entry(&device->pnp.ids, struct acpi_hardware_id, list); + return hid->id; +} + + + +static void acpi_add_id(struct acpi_device *device, const char *dev_id) +{ + struct acpi_hardware_id *id; + + id = kmalloc(sizeof(*id), GFP_KERNEL); + if (!id) + { + return; + }; + + INIT_LIST_HEAD(&id->list); + + id->id = kmalloc(strlen(dev_id) + 1, GFP_KERNEL); + if (!id->id) { + kfree(id); + return; + } + + strcpy(id->id, dev_id); + + list_add_tail(&id->list, &device->pnp.ids); +} + +#define ACPI_VIDEO_OUTPUT_SWITCHING 0x0001 +#define ACPI_VIDEO_DEVICE_POSTING 0x0002 +#define ACPI_VIDEO_ROM_AVAILABLE 0x0004 +#define ACPI_VIDEO_BACKLIGHT 0x0008 +#define ACPI_VIDEO_BACKLIGHT_FORCE_VENDOR 0x0010 +#define ACPI_VIDEO_BACKLIGHT_FORCE_VIDEO 0x0020 +#define ACPI_VIDEO_OUTPUT_SWITCHING_FORCE_VENDOR 0x0040 +#define ACPI_VIDEO_OUTPUT_SWITCHING_FORCE_VIDEO 0x0080 +#define ACPI_VIDEO_BACKLIGHT_DMI_VENDOR 0x0100 +#define ACPI_VIDEO_BACKLIGHT_DMI_VIDEO 0x0200 +#define ACPI_VIDEO_OUTPUT_SWITCHING_DMI_VENDOR 0x0400 +#define ACPI_VIDEO_OUTPUT_SWITCHING_DMI_VIDEO 0x0800 + + +long acpi_is_video_device(struct acpi_device *device) +{ + ACPI_HANDLE h_dummy; + long video_caps = 0; + + if (!device) + return 0; + + /* Is this device able to support video switching ? */ + if (ACPI_SUCCESS(AcpiGetHandle(device->handle, "_DOD", &h_dummy)) || + ACPI_SUCCESS(AcpiGetHandle(device->handle, "_DOS", &h_dummy))) + video_caps |= ACPI_VIDEO_OUTPUT_SWITCHING; + + /* Is this device able to retrieve a video ROM ? */ + if (ACPI_SUCCESS(AcpiGetHandle(device->handle, "_ROM", &h_dummy))) + video_caps |= ACPI_VIDEO_ROM_AVAILABLE; + + /* Is this device able to configure which video head to be POSTed ? */ + if (ACPI_SUCCESS(AcpiGetHandle(device->handle, "_VPO", &h_dummy)) && + ACPI_SUCCESS(AcpiGetHandle(device->handle, "_GPD", &h_dummy)) && + ACPI_SUCCESS(AcpiGetHandle(device->handle, "_SPD", &h_dummy))) + video_caps |= ACPI_VIDEO_DEVICE_POSTING; + + return video_caps; +} + + +static void acpi_device_set_id(struct acpi_device *device) +{ + ACPI_STATUS status; + ACPI_DEVICE_INFO *info; + ACPI_DEVICE_ID_LIST *cid_list; + int i; + + switch (device->device_type) + { + case ACPI_BUS_TYPE_DEVICE: + if (ACPI_IS_ROOT_DEVICE(device)) + { + acpi_add_id(device, ACPI_SYSTEM_HID); + break; + } + + status = AcpiGetObjectInfo(device->handle, &info); + if (ACPI_FAILURE(status)) { + printk(KERN_ERR "%s: Error reading device info\n", __func__); + return; + } + + if (info->Valid & ACPI_VALID_HID) + acpi_add_id(device, info->HardwareId.String); + if (info->Valid & ACPI_VALID_CID) + { + cid_list = &info->CompatibleIdList; + for (i = 0; i < cid_list->Count; i++) + acpi_add_id(device, cid_list->Ids[i].String); + } + if (info->Valid & ACPI_VALID_ADR) { + device->pnp.bus_address = info->Address; + device->flags.bus_address = 1; + } + + kfree(info); + + /* + * Some devices don't reliably have _HIDs & _CIDs, so add + * synthetic HIDs to make sure drivers can find them. + */ + if (acpi_is_video_device(device)) + acpi_add_id(device, ACPI_VIDEO_HID); + else if (ACPI_SUCCESS(acpi_bay_match(device))) + acpi_add_id(device, ACPI_BAY_HID); + else if (ACPI_SUCCESS(acpi_dock_match(device))) + acpi_add_id(device, ACPI_DOCK_HID); + else if (!acpi_device_hid(device) && + ACPI_IS_ROOT_DEVICE(device->parent)) { + acpi_add_id(device, ACPI_BUS_HID); /* \_SB, LNXSYBUS */ + strcpy(device->pnp.device_name, ACPI_BUS_DEVICE_NAME); + strcpy(device->pnp.device_class, ACPI_BUS_CLASS); + } + + break; + case ACPI_BUS_TYPE_POWER: + acpi_add_id(device, ACPI_POWER_HID); + break; + case ACPI_BUS_TYPE_PROCESSOR: + acpi_add_id(device, ACPI_PROCESSOR_OBJECT_HID); + break; + case ACPI_BUS_TYPE_THERMAL: + acpi_add_id(device, ACPI_THERMAL_HID); + break; + case ACPI_BUS_TYPE_POWER_BUTTON: + acpi_add_id(device, ACPI_BUTTON_HID_POWERF); + break; + case ACPI_BUS_TYPE_SLEEP_BUTTON: + acpi_add_id(device, ACPI_BUTTON_HID_SLEEPF); + break; + } + + /* + * We build acpi_devices for some objects that don't have _HID or _CID, + * e.g., PCI bridges and slots. Drivers can't bind to these objects, + * but we do use them indirectly by traversing the acpi_device tree. + * This generic ID isn't useful for driver binding, but it provides + * the useful property that "every acpi_device has an ID." + */ + if (list_empty(&device->pnp.ids)) + acpi_add_id(device, "device"); +} + + +static int acpi_device_set_context(struct acpi_device *device) +{ + ACPI_STATUS status; + + /* + * Context + * ------- + * Attach this 'struct acpi_device' to the ACPI object. This makes + * resolutions from handle->device very efficient. Fixed hardware + * devices have no handles, so we skip them. + */ + if (!device->handle) + return 0; + + status = AcpiAttachData(device->handle, + acpi_bus_data_handler, device); + if (ACPI_SUCCESS(status)) + return 0; + + dbgprintf(KERN_ERR "Error attaching device data\n"); + return -ENODEV; +} + + +static int acpi_device_register(struct acpi_device *device) +{ + int result; + struct acpi_device_bus_id *acpi_device_bus_id, *new_bus_id; + int found = 0; + + /* + * Linkage + * ------- + * Link this device to its parent and siblings. + */ + INIT_LIST_HEAD(&device->children); + INIT_LIST_HEAD(&device->node); + + new_bus_id = kzalloc(sizeof(struct acpi_device_bus_id), GFP_KERNEL); + if (!new_bus_id) { + dbgprintf(KERN_ERR "Memory allocation error\n"); + return -ENOMEM; + } + + mutex_lock(&acpi_device_lock); + /* + * Find suitable bus_id and instance number in acpi_bus_id_list + * If failed, create one and link it into acpi_bus_id_list + */ + list_for_each_entry(acpi_device_bus_id, &acpi_bus_id_list, node) + { + if (!strcmp(acpi_device_bus_id->bus_id, acpi_device_hid(device))) + { + acpi_device_bus_id->instance_no++; + found = 1; + kfree(new_bus_id); + break; + } + } + if (!found) + { + acpi_device_bus_id = new_bus_id; + strcpy(acpi_device_bus_id->bus_id, acpi_device_hid(device)); + acpi_device_bus_id->instance_no = 0; + list_add_tail(&acpi_device_bus_id->node, &acpi_bus_id_list); + } + +// dev_set_name(&device->dev, "%s:%02x", acpi_device_bus_id->bus_id, acpi_device_bus_id->instance_no); + + if (device->parent) + list_add_tail(&device->node, &device->parent->children); + + mutex_unlock(&acpi_device_lock); + +// device->dev.bus = &acpi_bus_type; +// device->dev.release = &acpi_device_release; +// result = device_register(&device->dev); +// if (result) { +// dev_err(&device->dev, "Error registering device\n"); +// goto end; +// } + + +// device->removal_type = ACPI_BUS_REMOVAL_NORMAL; + return 0; +end: + mutex_lock(&acpi_device_lock); + if (device->parent) + list_del(&device->node); + mutex_unlock(&acpi_device_lock); + return result; +} + + + +static int acpi_add_single_object(struct acpi_device **child, + ACPI_HANDLE handle, int type, + unsigned long long sta, + struct acpi_bus_ops *ops) +{ + int result; + struct acpi_device *device; + ACPI_BUFFER buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + + device = kzalloc(sizeof(struct acpi_device), GFP_KERNEL); + if (!device) { + dbgprintf("%s: Memory allocation error\n", __FUNCTION__); + return -ENOMEM; + } + + INIT_LIST_HEAD(&device->pnp.ids); + device->device_type = type; + device->handle = handle; + device->parent = acpi_bus_get_parent(handle); + device->bus_ops = *ops; /* workround for not call .start */ + STRUCT_TO_INT(device->status) = sta; + + acpi_device_get_busid(device); + + /* + * Flags + * ----- + * Note that we only look for object handles -- cannot evaluate objects + * until we know the device is present and properly initialized. + */ + result = acpi_bus_get_flags(device); + if (result) + goto end; + + /* + * Initialize Device + * ----------------- + * TBD: Synch with Core's enumeration/initialization process. + */ + acpi_device_set_id(device); + + + if ((result = acpi_device_set_context(device))) + goto end; + + result = acpi_device_register(device); + + /* + * Bind _ADR-Based Devices when hot add + */ +// if (device->flags.bus_address) { +// if (device->parent && device->parent->ops.bind) +// device->parent->ops.bind(device); +// } + +end: + if (!result) { + AcpiGetName(handle, ACPI_FULL_PATHNAME, &buffer); + dbgprintf("Adding [%s]\n", (char *)buffer.Pointer); + kfree(buffer.Pointer); + *child = device; + }; + return result; +} + + + + +static ACPI_STATUS acpi_bus_check_add(ACPI_HANDLE handle, u32 lvl, + void *context, void **return_value) +{ + struct acpi_bus_ops *ops = context; + int type; + unsigned long long sta; + struct acpi_device *device; + ACPI_STATUS status; + int result; + + result = acpi_bus_type_and_status(handle, &type, &sta); + + if (result) + { + return AE_OK; + }; + + if (!(sta & ACPI_STA_DEVICE_PRESENT) && + !(sta & ACPI_STA_DEVICE_FUNCTIONING)) + { + return AE_CTRL_DEPTH; + }; + + /* + * We may already have an acpi_device from a previous enumeration. If + * so, we needn't add it again, but we may still have to start it. + */ + device = NULL; + acpi_bus_get_device(handle, &device); + if (ops->acpi_op_add && !device) + acpi_add_single_object(&device, handle, type, sta, ops); + + if (!device) + { + return AE_CTRL_DEPTH; + }; +/* + if (ops->acpi_op_start && !(ops->acpi_op_add)) { + status = acpi_start_single_object(device); + if (ACPI_FAILURE(status)) + return AE_CTRL_DEPTH; + } +*/ + + if (!*return_value) + *return_value = device; + + return AE_OK; +} + + + +static int acpi_bus_scan(ACPI_HANDLE handle, struct acpi_bus_ops *ops, + struct acpi_device **child) +{ + ACPI_STATUS status; + void *device = NULL; + + ENTER(); + + status = acpi_bus_check_add(handle, 0, ops, &device); + + if (ACPI_SUCCESS(status)) + AcpiWalkNamespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, + acpi_bus_check_add, NULL, ops, &device); + + if (child) + *child = device; + + LEAVE(); + + if (device) + return 0; + else + return -ENODEV; +} + + + +int acpi_scan() +{ + int err; + struct acpi_bus_ops ops; + + memset(&ops, 0, sizeof(ops)); + ops.acpi_op_add = 1; + ops.acpi_op_start = 1; + + err = acpi_bus_scan(ACPI_ROOT_OBJECT, &ops, &acpi_root); + + return err; +}; + + +enum pic_mode +{ + IO_PIC = 0, + IO_APIC +}; + +static void set_pic_mode(enum pic_mode mode) +{ + ACPI_OBJECT arg1; + ACPI_OBJECT_LIST args; + ACPI_STATUS as; + + arg1.Type = ACPI_TYPE_INTEGER; + arg1.Integer.Value = mode; + args.Count = 1; + args.Pointer = &arg1; + + as = AcpiEvaluateObject(ACPI_ROOT_OBJECT, "_PIC", &args, NULL); + /* + * We can silently ignore failure as it may not be implemented, ACPI should + * provide us with correct information anyway + */ + if (ACPI_SUCCESS(as)) + dbgprintf("ACPI: machine set to %s mode\n", mode ? "APIC" : "PIC"); +} + +void print_device_tree(struct acpi_device *device) +{ + struct acpi_device *child; + + dbgprintf("%s\n", device->pnp.bus_id); + + list_for_each_entry(child, &device->children, node) + { + print_device_tree(child); + }; +}; + u32_t drvEntry(int action, char *cmdline) { u32_t retval; @@ -106,6 +951,12 @@ u32_t drvEntry(int action, char *cmdline) if(action != 1) return 0; + if( !dbg_open("/rd/1/drivers/acpi.log") ) + { + printf("Can't open /rd/1/drivers/acpi.log\nExit\n"); + return 0; + } + status = AcpiReallocateRootTable(); if (ACPI_FAILURE(status)) { dbgprintf("Unable to reallocate ACPI tables\n"); @@ -133,16 +984,16 @@ u32_t drvEntry(int action, char *cmdline) goto err; } - u32_t mode = ACPI_NO_HARDWARE_INIT | ACPI_NO_ACPI_ENABLE; +// u32_t mode = ACPI_NO_HARDWARE_INIT | ACPI_NO_ACPI_ENABLE; - status = AcpiEnableSubsystem(mode); + status = AcpiEnableSubsystem(0); if (status != AE_OK) { dbgprintf("AcpiEnableSubsystem failed (%s)\n", AcpiFormatException(status)); goto err; } - status = AcpiInitializeObjects (ACPI_FULL_INITIALIZATION); + status = AcpiInitializeObjects (0); if (ACPI_FAILURE (status)) { dbgprintf("AcpiInitializeObjects failed (%s)\n", @@ -150,10 +1001,99 @@ u32_t drvEntry(int action, char *cmdline) goto err; } - AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, 4, - get_device_by_hid_callback, NULL, NULL, NULL); + + set_pic_mode(IO_APIC); #if 0 + scan_devices(); + + { + bool retval = false; + u32_t bus, last_bus; + + if( (last_bus = PciApi(1))==-1) + return retval; + + dbgprintf("last bus %x\n", last_bus); + + for(bus=0; bus <= last_bus; bus++) + { + u32_t dev; + + for(dev = 0; dev < 32; dev++) + { + u32_t fn; + + for(fn = 0; fn < 8; fn++) + { + + u32_t id; + u32_t irq_bios, irq_acpi; + u32_t irq_pin; + u16_t pcicmd; + u32_t tmp; + + u32_t devfn = (dev<<3 )|fn; + + id = PciRead32(bus,devfn, PCI_VENDOR_ID); + + /* some broken boards return 0 or ~0 if a slot is empty: */ + if (id == 0xffffffff || id == 0x00000000 || + id == 0x0000ffff || id == 0xffff0000) + continue; + + pcicmd = PciRead16(bus,devfn, PCI_COMMAND); + if (! pcicmd & PCI_COMMAND_IO) + continue; + + tmp = PciRead32(bus,devfn, 0x3C); + + irq_bios = tmp & 0xFF; + irq_pin = (tmp >> 8) & 0xFF; + + int slot = (fn >> 3) & 0x1f; + + irq_acpi = irqtable[ dev * PCI_MAX_PINS +(irq_pin-1) ]; + + if( irq_acpi < 0) + dbgprintf("PCI: no ACPI IRQ routing for " + "device %d.%d.%d INT%c\n",bus,dev,fn,'A'+irq_pin-1); + + dbgprintf("pci device %x_%x bus %d dev %d fn %d," + "IRQ PIN %d BIOS IRQ %d ACPI IRQ %d\n", + id & 0xFFFF, id>>16, bus, dev, fn, irq_pin, irq_bios, irq_acpi); + }; + } + }; + }; +#endif + + acpi_scan(); + + print_device_tree(acpi_root); + +/* + ACPI_HANDLE bus_handle; + ACPI_HANDLE pci_root; + + status = AcpiGetHandle(0, "\\_SB_", &bus_handle); + dbgprintf("system bus handle %x\n", bus_handle); + + status = AcpiGetHandle(bus_handle, "PCI0", &pci_root); + if (status != AE_OK) { + dbgprintf("AcpiGetHandle failed (%s)\n", + AcpiFormatException(status)); + goto err; + } + + AcpiWalkNamespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, 100, + get_device_by_hid_callback, NULL, NULL, NULL); +*/ + +#if 0 + + AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, 4, + get_device_by_hid_callback, NULL, NULL, NULL); ACPI_OBJECT obj; ACPI_HANDLE bus_handle; @@ -242,3 +1182,200 @@ err: }; + +#if 0 + +ACPI_STATUS +get_device_by_hid_callback(ACPI_HANDLE obj, u32_t depth, void* context, + void** retval) +{ + static u32_t counter = 0; + static char buff[256]; + + ACPI_STATUS status; + + ACPI_BUFFER buffer; + + ACPI_DEVICE_INFO *info; + + // *retval = NULL; + + buffer.Length = 255; + buffer.Pointer = buff; + + status = AcpiGetName(obj, ACPI_FULL_PATHNAME, &buffer); + if (status != AE_OK) { + return AE_CTRL_TERMINATE; + } + + buff[buffer.Length] = '\0'; + + dbgprintf("device %d %s ", counter, buff); + + status = AcpiGetObjectInfo(obj, &info); + + if (ACPI_SUCCESS (status)) + { + if (info->Valid & ACPI_VALID_HID) + dbgprintf (" HID: %s", info->HardwareId.String); + + }; + + dbgprintf("\n"); + counter++; + + return AE_OK; +} + +prt_walk_table(ACPI_BUFFER *prt) +{ + ACPI_PCI_ROUTING_TABLE *entry; + char *prtptr; + + /* First check to see if there is a table to walk. */ + if (prt == NULL || prt->Pointer == NULL) + return; + + /* Walk the table executing the handler function for each entry. */ + prtptr = prt->Pointer; + entry = (ACPI_PCI_ROUTING_TABLE *)prtptr; + while (entry->Length != 0) + { + + dbgprintf("adress: %x %x ", (u32_t)(entry->Address>>32), + (u32_t)entry->Address); + dbgprintf("pin: %d index: %d source: %s\n", + entry->Pin, + entry->SourceIndex, + entry->Source); + +// handler(entry, arg); + prtptr += entry->Length; + entry = (ACPI_PCI_ROUTING_TABLE *)prtptr; + } +} + + +static void add_irq(unsigned dev, unsigned pin, u8_t irq) +{ +// assert(dev < PCI_MAX_DEVICES && pin < PCI_MAX_PINS); + + irqtable[dev * PCI_MAX_PINS + pin] = irq; +} + +static ACPI_STATUS get_irq_resource(ACPI_RESOURCE *res, void *context) +{ + ACPI_PCI_ROUTING_TABLE *tbl = (ACPI_PCI_ROUTING_TABLE *) context; + + if (res->Type == ACPI_RESOURCE_TYPE_IRQ) + { + ACPI_RESOURCE_IRQ *irq; + + irq = &res->Data.Irq; + add_irq(tbl->Address >> 16, tbl->Pin, + irq->Interrupts[tbl->SourceIndex]); + } else if (res->Type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ) + { + ACPI_RESOURCE_EXTENDED_IRQ *irq; + + add_irq(tbl->Address >> 16, tbl->Pin, + irq->Interrupts[tbl->SourceIndex]); + } + + return AE_OK; +} + +char buff[4096]; + +static ACPI_STATUS get_pci_irq_routing(ACPI_HANDLE handle) +{ + ACPI_STATUS status; + ACPI_BUFFER abuff; + ACPI_PCI_ROUTING_TABLE *tbl; + + abuff.Length = sizeof(buff); + abuff.Pointer = buff; + + status = AcpiGetIrqRoutingTable(handle, &abuff); + if (ACPI_FAILURE(status)) { + return AE_OK; + } + + for (tbl = (ACPI_PCI_ROUTING_TABLE *)abuff.Pointer; tbl->Length; + tbl = (ACPI_PCI_ROUTING_TABLE *) + ((char *)tbl + tbl->Length)) + { + ACPI_HANDLE src_handle; + + if (*(char*)tbl->Source == '\0') { + add_irq(tbl->Address >> 16, tbl->Pin, tbl->SourceIndex); + continue; + } + + status = AcpiGetHandle(handle, tbl->Source, &src_handle); + if (ACPI_FAILURE(status)) { + printf("Failed AcpiGetHandle\n"); + continue; + } + status = AcpiWalkResources(src_handle, METHOD_NAME__CRS, + get_irq_resource, tbl); + if (ACPI_FAILURE(status)) { + printf("Failed IRQ resource\n"); + continue; + } + } + + return AE_OK; +} + +static ACPI_STATUS add_pci_root_dev(ACPI_HANDLE handle, + UINT32 level, + void *context, + void **retval) +{ + int i; + static unsigned called; + + if (++called > 1) { + dbgprintf("ACPI: Warning! Multi rooted PCI is not supported!\n"); + return AE_OK; + } + + for (i = 0; i < IRQ_TABLE_ENTRIES; i++) + irqtable[i] = -1; + + return get_pci_irq_routing(handle); +} + +static ACPI_STATUS add_pci_dev(ACPI_HANDLE handle, + UINT32 level, + void *context, + void **retval) +{ + /* skip pci root when we get to it again */ + if (handle == pci_root_handle) + return AE_OK; + + return get_pci_irq_routing(handle); +} + +static void scan_devices(void) +{ + ACPI_STATUS status; + + /* get the root first */ + status = AcpiGetDevices("PNP0A03", add_pci_root_dev, NULL, NULL); + if (status != AE_OK) { + dbgprintf("scan_devices failed (%s)\n", + AcpiFormatException(status)); + return; + } + +// assert(ACPI_SUCCESS(status)); + + /* get the rest of the devices that implement _PRT */ + status = AcpiGetDevices(NULL, add_pci_dev, NULL, NULL); +// assert(ACPI_SUCCESS(status)); +} + +#endif