Merge tag 'for-linus-4.8-rc0-tag' of git://git.kernel.org/pub/scm/linux/kernel/git...
[sfrench/cifs-2.6.git] / drivers / acpi / scan.c
index 5f28cf7783490e1da56d12378d7c3fba0e52185e..ad9fc84a8601206cec6cc1fb15cb36e5c23250cb 100644 (file)
@@ -46,6 +46,13 @@ DEFINE_MUTEX(acpi_device_lock);
 LIST_HEAD(acpi_wakeup_device_list);
 static DEFINE_MUTEX(acpi_hp_context_lock);
 
+/*
+ * The UART device described by the SPCR table is the only object which needs
+ * special-casing. Everything else is covered by ACPI namespace paths in STAO
+ * table.
+ */
+static u64 spcr_uart_addr;
+
 struct acpi_dep_data {
        struct list_head node;
        acpi_handle master;
@@ -494,6 +501,8 @@ static void acpi_device_del(struct acpi_device *device)
        device_del(&device->dev);
 }
 
+static BLOCKING_NOTIFIER_HEAD(acpi_reconfig_chain);
+
 static LIST_HEAD(acpi_device_del_list);
 static DEFINE_MUTEX(acpi_device_del_lock);
 
@@ -514,6 +523,9 @@ static void acpi_device_del_work_fn(struct work_struct *work_not_used)
 
                mutex_unlock(&acpi_device_del_lock);
 
+               blocking_notifier_call_chain(&acpi_reconfig_chain,
+                                            ACPI_RECONFIG_DEVICE_REMOVE, adev);
+
                acpi_device_del(adev);
                /*
                 * Drop references to all power resources that might have been
@@ -1406,7 +1418,7 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle,
        acpi_bus_get_flags(device);
        device->flags.match_driver = false;
        device->flags.initialized = true;
-       device->flags.visited = false;
+       acpi_device_clear_enumerated(device);
        device_initialize(&device->dev);
        dev_set_uevent_suppress(&device->dev, true);
        acpi_init_coherency(device);
@@ -1453,6 +1465,41 @@ static int acpi_add_single_object(struct acpi_device **child,
        return 0;
 }
 
+static acpi_status acpi_get_resource_memory(struct acpi_resource *ares,
+                                           void *context)
+{
+       struct resource *res = context;
+
+       if (acpi_dev_resource_memory(ares, res))
+               return AE_CTRL_TERMINATE;
+
+       return AE_OK;
+}
+
+static bool acpi_device_should_be_hidden(acpi_handle handle)
+{
+       acpi_status status;
+       struct resource res;
+
+       /* Check if it should ignore the UART device */
+       if (!(spcr_uart_addr && acpi_has_method(handle, METHOD_NAME__CRS)))
+               return false;
+
+       /*
+        * The UART device described in SPCR table is assumed to have only one
+        * memory resource present. So we only look for the first one here.
+        */
+       status = acpi_walk_resources(handle, METHOD_NAME__CRS,
+                                    acpi_get_resource_memory, &res);
+       if (ACPI_FAILURE(status) || res.start != spcr_uart_addr)
+               return false;
+
+       acpi_handle_info(handle, "The UART device @%pa in SPCR table will be hidden\n",
+                        &res.start);
+
+       return true;
+}
+
 static int acpi_bus_type_and_status(acpi_handle handle, int *type,
                                    unsigned long long *sta)
 {
@@ -1466,6 +1513,9 @@ static int acpi_bus_type_and_status(acpi_handle handle, int *type,
        switch (acpi_type) {
        case ACPI_TYPE_ANY:             /* for ACPI_ROOT_OBJECT */
        case ACPI_TYPE_DEVICE:
+               if (acpi_device_should_be_hidden(handle))
+                       return -ENODEV;
+
                *type = ACPI_BUS_TYPE_DEVICE;
                status = acpi_bus_get_status_handle(handle, sta);
                if (ACPI_FAILURE(status))
@@ -1676,15 +1726,20 @@ static void acpi_default_enumeration(struct acpi_device *device)
        bool is_spi_i2c_slave = false;
 
        /*
-        * Do not enemerate SPI/I2C slaves as they will be enuerated by their
+        * Do not enumerate SPI/I2C slaves as they will be enumerated by their
         * respective parents.
         */
        INIT_LIST_HEAD(&resource_list);
        acpi_dev_get_resources(device, &resource_list, acpi_check_spi_i2c_slave,
                               &is_spi_i2c_slave);
        acpi_dev_free_resource_list(&resource_list);
-       if (!is_spi_i2c_slave)
+       if (!is_spi_i2c_slave) {
                acpi_create_platform_device(device);
+               acpi_device_set_enumerated(device);
+       } else {
+               blocking_notifier_call_chain(&acpi_reconfig_chain,
+                                            ACPI_RECONFIG_DEVICE_ADD, device);
+       }
 }
 
 static const struct acpi_device_id generic_device_ids[] = {
@@ -1751,7 +1806,7 @@ static void acpi_bus_attach(struct acpi_device *device)
        acpi_bus_get_status(device);
        /* Skip devices that are not present. */
        if (!acpi_device_is_present(device)) {
-               device->flags.visited = false;
+               acpi_device_clear_enumerated(device);
                device->flags.power_manageable = 0;
                return;
        }
@@ -1766,7 +1821,7 @@ static void acpi_bus_attach(struct acpi_device *device)
 
                device->flags.initialized = true;
        }
-       device->flags.visited = false;
+
        ret = acpi_scan_attach_handler(device);
        if (ret < 0)
                return;
@@ -1780,7 +1835,6 @@ static void acpi_bus_attach(struct acpi_device *device)
                if (!ret && device->pnp.type.platform_id)
                        acpi_default_enumeration(device);
        }
-       device->flags.visited = true;
 
  ok:
        list_for_each_entry(child, &device->children, node)
@@ -1872,7 +1926,7 @@ void acpi_bus_trim(struct acpi_device *adev)
         */
        acpi_device_set_power(adev, ACPI_STATE_D3_COLD);
        adev->flags.initialized = false;
-       adev->flags.visited = false;
+       acpi_device_clear_enumerated(adev);
 }
 EXPORT_SYMBOL_GPL(acpi_bus_trim);
 
@@ -1916,9 +1970,26 @@ static int acpi_bus_scan_fixed(void)
        return result < 0 ? result : 0;
 }
 
+static void __init acpi_get_spcr_uart_addr(void)
+{
+       acpi_status status;
+       struct acpi_table_spcr *spcr_ptr;
+
+       status = acpi_get_table(ACPI_SIG_SPCR, 0,
+                               (struct acpi_table_header **)&spcr_ptr);
+       if (ACPI_SUCCESS(status))
+               spcr_uart_addr = spcr_ptr->serial_port.address;
+       else
+               printk(KERN_WARNING PREFIX "STAO table present, but SPCR is missing\n");
+}
+
+static bool acpi_scan_initialized;
+
 int __init acpi_scan_init(void)
 {
        int result;
+       acpi_status status;
+       struct acpi_table_stao *stao_ptr;
 
        acpi_pci_root_init();
        acpi_pci_link_init();
@@ -1934,6 +2005,20 @@ int __init acpi_scan_init(void)
 
        acpi_scan_add_handler(&generic_device_handler);
 
+       /*
+        * If there is STAO table, check whether it needs to ignore the UART
+        * device in SPCR table.
+        */
+       status = acpi_get_table(ACPI_SIG_STAO, 0,
+                               (struct acpi_table_header **)&stao_ptr);
+       if (ACPI_SUCCESS(status)) {
+               if (stao_ptr->header.length > sizeof(struct acpi_table_stao))
+                       printk(KERN_INFO PREFIX "STAO Name List not yet supported.");
+
+               if (stao_ptr->ignore_uart)
+                       acpi_get_spcr_uart_addr();
+       }
+
        mutex_lock(&acpi_scan_lock);
        /*
         * Enumerate devices in the ACPI namespace.
@@ -1960,6 +2045,8 @@ int __init acpi_scan_init(void)
 
        acpi_update_all_gpes();
 
+       acpi_scan_initialized = true;
+
  out:
        mutex_unlock(&acpi_scan_lock);
        return result;
@@ -2003,3 +2090,57 @@ int __init __acpi_probe_device_table(struct acpi_probe_entry *ap_head, int nr)
 
        return count;
 }
+
+struct acpi_table_events_work {
+       struct work_struct work;
+       void *table;
+       u32 event;
+};
+
+static void acpi_table_events_fn(struct work_struct *work)
+{
+       struct acpi_table_events_work *tew;
+
+       tew = container_of(work, struct acpi_table_events_work, work);
+
+       if (tew->event == ACPI_TABLE_EVENT_LOAD) {
+               acpi_scan_lock_acquire();
+               acpi_bus_scan(ACPI_ROOT_OBJECT);
+               acpi_scan_lock_release();
+       }
+
+       kfree(tew);
+}
+
+void acpi_scan_table_handler(u32 event, void *table, void *context)
+{
+       struct acpi_table_events_work *tew;
+
+       if (!acpi_scan_initialized)
+               return;
+
+       if (event != ACPI_TABLE_EVENT_LOAD)
+               return;
+
+       tew = kmalloc(sizeof(*tew), GFP_KERNEL);
+       if (!tew)
+               return;
+
+       INIT_WORK(&tew->work, acpi_table_events_fn);
+       tew->table = table;
+       tew->event = event;
+
+       schedule_work(&tew->work);
+}
+
+int acpi_reconfig_notifier_register(struct notifier_block *nb)
+{
+       return blocking_notifier_chain_register(&acpi_reconfig_chain, nb);
+}
+EXPORT_SYMBOL(acpi_reconfig_notifier_register);
+
+int acpi_reconfig_notifier_unregister(struct notifier_block *nb)
+{
+       return blocking_notifier_chain_unregister(&acpi_reconfig_chain, nb);
+}
+EXPORT_SYMBOL(acpi_reconfig_notifier_unregister);