1,9 → 1,31 |
|
#include <types.h> |
#include <ddk.h> |
#include <linux/errno.h> |
#include <mutex.h> |
#include <pci.h> |
#include <syscall.h> |
|
#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,7 → 37,56 |
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 |
|
#define acpi_remap( addr ) MapIoMem((void*)(addr),4096, 0x01) |
|
|
struct acpi_bus_ops |
{ |
u32_t acpi_op_add:1; |
u32_t acpi_op_start:1; |
}; |
|
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; |
}; |
|
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; |
}; |
|
|
typedef char acpi_bus_id[8]; |
typedef unsigned long acpi_bus_address; |
typedef char acpi_device_name[40]; |
typedef char acpi_device_class[20]; |
|
struct acpi_hardware_id { |
struct list_head list; |
char *id; |
}; |
|
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 |
get_device_by_hid_callback(ACPI_HANDLE obj, u32_t depth, void* context, |
void** retval) |
acpi_evaluate_integer(ACPI_HANDLE handle, ACPI_STRING pathname, |
ACPI_OBJECT_LIST *arguments, unsigned long long *data) |
{ |
static u32_t counter = 0; |
static char buff[256]; |
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; |
} |
|
if (element.Type != ACPI_TYPE_INTEGER) { |
acpi_util_eval_error(handle, pathname, AE_BAD_DATA); |
return AE_BAD_DATA; |
} |
|
*data = element.Integer.Value; |
|
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Return value [%llu]\n", *data)); |
|
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) |
{ |
return -EINVAL; |
}; |
|
/* TBD: Support fixed-feature devices */ |
|
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; |
} |
|
|
ACPI_STATUS acpi_bus_get_status_handle(ACPI_HANDLE handle, |
unsigned long long *sta) |
{ |
ACPI_STATUS status; |
|
ACPI_BUFFER buffer; |
status = acpi_evaluate_integer(handle, "_STA", NULL, sta); |
if (ACPI_SUCCESS(status)) |
{ |
return AE_OK; |
}; |
|
ACPI_DEVICE_INFO *info; |
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; |
} |
|
// *retval = NULL; |
|
buffer.Length = 255; |
buffer.Pointer = buff; |
|
status = AcpiGetName(obj, ACPI_FULL_PATHNAME, &buffer); |
if (status != AE_OK) { |
return AE_CTRL_TERMINATE; |
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; |
} |
|
buff[buffer.Length] = '\0'; |
return 0; |
} |
|
dbgprintf("device %d %s ", counter, buff); |
static struct acpi_device *acpi_bus_get_parent(ACPI_HANDLE handle) |
{ |
ACPI_STATUS status; |
struct acpi_device *device; |
int ret; |
|
status = AcpiGetObjectInfo(obj, &info); |
/* |
* 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) |
{ |
if (info->Valid & ACPI_VALID_HID) |
dbgprintf (" HID: %s", info->HardwareId.String); |
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; |
}; |
|
dbgprintf("\n"); |
counter++; |
INIT_LIST_HEAD(&id->list); |
|
return AE_OK; |
id->id = kmalloc(strlen(dev_id) + 1, GFP_KERNEL); |
if (!id->id) { |
kfree(id); |
return; |
} |
|
prt_walk_table(ACPI_BUFFER *prt) |
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_PCI_ROUTING_TABLE *entry; |
char *prtptr; |
ACPI_HANDLE h_dummy; |
long video_caps = 0; |
|
/* First check to see if there is a table to walk. */ |
if (prt == NULL || prt->Pointer == NULL) |
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; |
} |
|
/* Walk the table executing the handler function for each entry. */ |
prtptr = prt->Pointer; |
entry = (ACPI_PCI_ROUTING_TABLE *)prtptr; |
while (entry->Length != 0) |
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; |
} |
|
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); |
kfree(info); |
|
// handler(entry, arg); |
prtptr += entry->Length; |
entry = (ACPI_PCI_ROUTING_TABLE *)prtptr; |
/* |
* 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 |
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,9 → 984,9 |
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)); |
142,7 → 993,7 |
goto err; |
} |
|
status = AcpiInitializeObjects (ACPI_FULL_INITIALIZATION); |
status = AcpiInitializeObjects (0); |
if (ACPI_FAILURE (status)) |
{ |
dbgprintf("AcpiInitializeObjects failed (%s)\n", |
150,11 → 1001,100 |
goto err; |
} |
|
AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, 4, |
|
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; |
ACPI_HANDLE pci_root; |
242,3 → 1182,200 |
|
}; |
|
|
#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 |