2016-04-19 22:39:35

by Octavian Purdila

[permalink] [raw]
Subject: [RFC PATCH v2 00/10] ACPI overlays

This patch set enables custom ACPI board configuration by adding
mechanisms in the Linux kernel for loading user defined SSDTs.

In order to support ACPI open-ended hardware configurations we need a
way to augment the ACPI configuration provided by the firmware
image. A common example is connecting sensors on I2C / SPI buses on
development boards.

Although this can be accomplished by creating a kernel platform driver
or recompiling the firmware image with updated ACPI tables, neither is
practical: the former proliferates board specific kernel code while
the latter requires access to firmware tools which are often not
publicly available.

Because ACPI supports external references in AML code a more practical
way to augment firmware ACPI configuration is by dynamically loading
user defined SSDT tables that contain the board specific information.

This patch sets provides three methods for loading custom SSDTs:

* From an EFI variable

This is the preferred method, when EFI is supported on the platform,
because it allows a persistent, OS independent way of storing and
updating the user defined SSDTs. There is also work underway to
implement EFI support for loading user defined SSDTs and using this
method will make it easier to convert to the EFI loading mechanism
when that will arrive.

* From the first uncompressed initrd (similar with the override
functionality)

This is useful when EFI is not supported on the platform and when it
is not possible to defer the loading to userspace.

* From userspace via configfs

This is useful when we want to defer the operation to userspace for
platform detection, loading the SSDTs from a custom partition, etc.


Changes from v1:

* rebased on top of the ACPI install from initrd table functionality;
there is significant overlap between the 1st patch in this series
and these patch [1] from Lv - I kept it in this series until the
discussions around the taint and config option are resolved

* make sure EFI_RUNTIME_SERVICES are available before trying to use
EFI variables to load tables

* rework the ACPI reconfiguration notifications to work on device
granularity (device added or removed) instead of table granularity
(table loaded or unloaded)

* add support for table unloading / device removal

* note that the last patch is just a hack to be able to test the table
unload / device remove functionality, if someone wants to try out
this patch set

[1] https://patchwork.kernel.org/patch/8795931/


Octavian Purdila (10):
kernel: add TAINT_OVERLAY_ACPI_TABLE
acpi: decouple initrd table install from
CONFIG_ACPI_INITRD_TABLE_OVERRIDE
acpi: fix enumeration (visited) flags for bus rescans
acpi: add support for ACPI reconfiguration notifiers
i2c: add support for ACPI reconfigure notifications
spi: add support for ACPI reconfigure notifications
efi: load SSTDs from EFI variables
acpi: add support for configfs
acpi: add support for loading SSDTs via configfs
HACK: acpi: configfs: add unload_hanlde_path attribute for tables

Documentation/ABI/testing/configfs-acpi | 23 ++++
Documentation/acpi/ssdt-overlays.txt | 171 +++++++++++++++++++++++++++
Documentation/kernel-parameters.txt | 7 ++
Documentation/oops-tracing.txt | 2 +
Documentation/sysctl/kernel.txt | 1 +
MAINTAINERS | 1 +
arch/x86/kernel/setup.c | 2 +-
drivers/acpi/Kconfig | 9 ++
drivers/acpi/Makefile | 1 +
drivers/acpi/bus.c | 9 ++
drivers/acpi/configfs.c | 198 ++++++++++++++++++++++++++++++++
drivers/acpi/internal.h | 3 +
drivers/acpi/osl.c | 80 +++++++------
drivers/acpi/scan.c | 79 ++++++++++++-
drivers/acpi/sysfs.c | 6 +-
drivers/firmware/efi/efi.c | 109 ++++++++++++++++++
drivers/i2c/i2c-core.c | 170 +++++++++++++++++++++------
drivers/spi/spi.c | 94 +++++++++++++--
include/acpi/acpi_bus.h | 18 +++
include/linux/acpi.h | 8 +-
include/linux/kernel.h | 1 +
kernel/panic.c | 2 +
22 files changed, 895 insertions(+), 99 deletions(-)
create mode 100644 Documentation/ABI/testing/configfs-acpi
create mode 100644 Documentation/acpi/ssdt-overlays.txt
create mode 100644 drivers/acpi/configfs.c

--
1.9.1


2016-04-19 22:39:39

by Octavian Purdila

[permalink] [raw]
Subject: [RFC PATCH v2 01/10] kernel: add TAINT_OVERLAY_ACPI_TABLE

Add a new tain flag that indicates wheather the user has loaded ACPI
SSDT overlays. This will provide a clean indication in bug reports that
the user has added new information to the ACPI tables.

Signed-off-by: Octavian Purdila <[email protected]>
---
Documentation/acpi/ssdt-overlays.txt | 64 ++++++++++++++++++++++++++++++++++++
Documentation/oops-tracing.txt | 2 ++
Documentation/sysctl/kernel.txt | 1 +
include/linux/kernel.h | 1 +
kernel/panic.c | 2 ++
5 files changed, 70 insertions(+)
create mode 100644 Documentation/acpi/ssdt-overlays.txt

diff --git a/Documentation/acpi/ssdt-overlays.txt b/Documentation/acpi/ssdt-overlays.txt
new file mode 100644
index 0000000..54ee74a
--- /dev/null
+++ b/Documentation/acpi/ssdt-overlays.txt
@@ -0,0 +1,64 @@
+
+In order to support ACPI open-ended hardware configurations (e.g. development
+boards) we need a way to augment the ACPI configuration provided by the firmware
+image. A common example is connecting sensors on I2C / SPI buses on development
+boards.
+
+Although this can be accomplished by creating a kernel platform driver or
+recompiling the firmware image with updated ACPI tables, neither is practical:
+the former proliferates board specific kernel code while the latter requires
+access to firmware tools which are often not publicly available.
+
+Because ACPI supports external references in AML code a more practical
+way to augment firmware ACPI configuration is by dynamically loading
+user defined SSDT tables that contain the board specific information.
+
+For example, to enumerate a Bosch BMA222E accelerometer on the I2C bus of the
+Minnowboard MAX development board exposed via the LSE connector [1], the
+following ASL code can be used:
+
+DefinitionBlock ("minnowmax.aml", "SSDT", 1, "Vendor", "Accel", 0x00000003)
+{
+ External (\_SB.I2C6, DeviceObj)
+
+ Scope (\_SB.I2C6)
+ {
+ Device (STAC)
+ {
+ Name (_ADR, Zero)
+ Name (_HID, "BMA222E")
+
+ Method (_CRS, 0, Serialized)
+ {
+ Name (RBUF, ResourceTemplate ()
+ {
+ I2cSerialBus (0x0018, ControllerInitiated, 0x00061A80,
+ AddressingMode7Bit, "\\_SB.I2C6", 0x00,
+ ResourceConsumer, ,)
+ GpioInt (Edge, ActiveHigh, Exclusive, PullDown, 0x0000,
+ "\\_SB.GPO2", 0x00, ResourceConsumer, , )
+ { // Pin list
+ 0
+ }
+ })
+ Return (RBUF)
+ }
+ }
+ }
+}
+
+which can then be compiled to AML binary format:
+
+$ iasl minnowmax.asl
+
+Intel ACPI Component Architecture
+ASL Optimizing Compiler version 20140214-64 [Mar 29 2014]
+Copyright (c) 2000 - 2014 Intel Corporation
+
+ASL Input: minnomax.asl - 30 lines, 614 bytes, 7 keywords
+AML Output: minnowmax.aml - 165 bytes, 6 named objects, 1 executable opcodes
+
+[1] http://wiki.minnowboard.org/MinnowBoard_MAX#Low_Speed_Expansion_Connector_.28Top.29
+
+The resulting AML code can then be loaded by the kernel using one of the methods
+below.
diff --git a/Documentation/oops-tracing.txt b/Documentation/oops-tracing.txt
index f3ac05c..40e1117 100644
--- a/Documentation/oops-tracing.txt
+++ b/Documentation/oops-tracing.txt
@@ -272,6 +272,8 @@ characters, each representing a particular tainted value.

16: 'K' if the kernel has been live patched.

+ 17: 'N' if ACPI SSDT overlays have been loaded.
+
The primary reason for the 'Tainted: ' string is to tell kernel
debuggers if this is a clean kernel or if anything unusual has
occurred. Tainting is permanent: even if an offending module is
diff --git a/Documentation/sysctl/kernel.txt b/Documentation/sysctl/kernel.txt
index 57653a4..e68d4168 100644
--- a/Documentation/sysctl/kernel.txt
+++ b/Documentation/sysctl/kernel.txt
@@ -917,6 +917,7 @@ can be ORed together:
signature.
16384 - A soft lockup has previously occurred on the system.
32768 - The kernel has been live patched.
+65536 - ACPI SSDT overlays have been loaded.

==============================================================

diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index 2f7775e..8c90125 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -502,6 +502,7 @@ extern enum system_states {
#define TAINT_UNSIGNED_MODULE 13
#define TAINT_SOFTLOCKUP 14
#define TAINT_LIVEPATCH 15
+#define TAINT_OVERLAY_ACPI_TABLE 16

extern const char hex_asc[];
#define hex_asc_lo(x) hex_asc[((x) & 0x0f)]
diff --git a/kernel/panic.c b/kernel/panic.c
index 535c965..1ff0819 100644
--- a/kernel/panic.c
+++ b/kernel/panic.c
@@ -284,6 +284,7 @@ static const struct tnt tnts[] = {
{ TAINT_UNSIGNED_MODULE, 'E', ' ' },
{ TAINT_SOFTLOCKUP, 'L', ' ' },
{ TAINT_LIVEPATCH, 'K', ' ' },
+ { TAINT_OVERLAY_ACPI_TABLE, 'N', ' ' },
};

/**
@@ -305,6 +306,7 @@ static const struct tnt tnts[] = {
* 'E' - Unsigned module has been loaded.
* 'L' - A soft lockup has previously occurred.
* 'K' - Kernel has been live patched.
+ * 'N' - ACPI SSDT overlays have been loaded.
*
* The string is overwritten by the next call to print_tainted().
*/
--
1.9.1

2016-04-19 22:39:51

by Octavian Purdila

[permalink] [raw]
Subject: [RFC PATCH v2 04/10] acpi: add support for ACPI reconfiguration notifiers

Add support for ACPI reconfiguration notifiers to allow subsystems to
react to changes in the ACPI tables that happen after the initial
enumeration. This is similar with the way dynamic device tree
notifications work.

The reconfigure notifications supported for now are device add and
device remove.

Since ACPICA allows only one table notification handler, this patch
makes the table notifier function generic and moves it out of the
sysfs specific code.

Signed-off-by: Octavian Purdila <[email protected]>
---
drivers/acpi/bus.c | 9 +++++++
drivers/acpi/internal.h | 3 +++
drivers/acpi/scan.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++-
drivers/acpi/sysfs.c | 6 ++---
include/acpi/acpi_bus.h | 18 +++++++++++++
5 files changed, 99 insertions(+), 5 deletions(-)

diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index c068c82..dcac6f8 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -988,6 +988,13 @@ void __init acpi_subsystem_init(void)
}
}

+static acpi_status acpi_bus_table_handler(u32 event, void *table, void *context)
+{
+ acpi_scan_table_handler(event, table, context);
+
+ return acpi_sysfs_table_handler(event, table, context);
+}
+
static int __init acpi_bus_init(void)
{
int result;
@@ -1032,6 +1039,8 @@ static int __init acpi_bus_init(void)
* _PDC control method may load dynamic SSDT tables,
* and we need to install the table handler before that.
*/
+ status = acpi_install_table_handler(acpi_bus_table_handler, NULL);
+
acpi_sysfs_init();

acpi_early_processor_set_pdc();
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index 7c18847..ba878d8 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -86,6 +86,9 @@ bool acpi_queue_hotplug_work(struct work_struct *work);
void acpi_device_hotplug(struct acpi_device *adev, u32 src);
bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent);

+acpi_status acpi_sysfs_table_handler(u32 event, void *table, void *context);
+void acpi_scan_table_handler(u32 event, void *table, void *context);
+
/* --------------------------------------------------------------------------
Device Node Initialization / Removal
-------------------------------------------------------------------------- */
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 7656ea4..343c105 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -494,6 +494,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 +516,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
@@ -1676,7 +1681,7 @@ static bool 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 enemerate SPI/I2C slaves as they will be enumerated by their
* respective parents.
*/
INIT_LIST_HEAD(&resource_list);
@@ -1685,6 +1690,9 @@ static bool acpi_default_enumeration(struct acpi_device *device)
acpi_dev_free_resource_list(&resource_list);
if (!is_spi_i2c_slave)
acpi_create_platform_device(device);
+ else
+ blocking_notifier_call_chain(&acpi_reconfig_chain,
+ ACPI_RECONFIG_DEVICE_ADD, device);
return !is_spi_i2c_slave;
}

@@ -1919,6 +1927,8 @@ static int acpi_bus_scan_fixed(void)
return result < 0 ? result : 0;
}

+static bool acpi_scan_initialized;
+
int __init acpi_scan_init(void)
{
int result;
@@ -1963,6 +1973,8 @@ int __init acpi_scan_init(void)

acpi_update_all_gpes();

+ acpi_scan_initialized = true;
+
out:
mutex_unlock(&acpi_scan_lock);
return result;
@@ -2006,3 +2018,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;
+};
+
+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);
diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c
index 0243d37..d132d66 100644
--- a/drivers/acpi/sysfs.c
+++ b/drivers/acpi/sysfs.c
@@ -378,8 +378,7 @@ static void acpi_table_attr_init(struct acpi_table_attr *table_attr,
return;
}

-static acpi_status
-acpi_sysfs_table_handler(u32 event, void *table, void *context)
+acpi_status acpi_sysfs_table_handler(u32 event, void *table, void *context)
{
struct acpi_table_attr *table_attr;

@@ -452,9 +451,8 @@ static int acpi_tables_sysfs_init(void)

kobject_uevent(tables_kobj, KOBJ_ADD);
kobject_uevent(dynamic_tables_kobj, KOBJ_ADD);
- status = acpi_install_table_handler(acpi_sysfs_table_handler, NULL);

- return ACPI_FAILURE(status) ? -EINVAL : 0;
+ return 0;
err_dynamic_tables:
kobject_put(tables_kobj);
err:
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h
index 14362a8..05ba5d4 100644
--- a/include/acpi/acpi_bus.h
+++ b/include/acpi/acpi_bus.h
@@ -518,6 +518,14 @@ int acpi_match_device_ids(struct acpi_device *device,
int acpi_create_dir(struct acpi_device *);
void acpi_remove_dir(struct acpi_device *);

+enum acpi_reconfig_event {
+ ACPI_RECONFIG_DEVICE_ADD = 0,
+ ACPI_RECONFIG_DEVICE_REMOVE,
+};
+
+int acpi_reconfig_notifier_register(struct notifier_block *nb);
+int acpi_reconfig_notifier_unregister(struct notifier_block *nb);
+
static inline bool acpi_device_enumerated(struct acpi_device *adev)
{
return adev && adev->flags.initialized && adev->flags.visited;
@@ -643,6 +651,16 @@ static inline bool acpi_device_can_poweroff(struct acpi_device *adev)
static inline int register_acpi_bus_type(void *bus) { return 0; }
static inline int unregister_acpi_bus_type(void *bus) { return 0; }

+static inline int acpi_reconfig_notifier_register(struct notifier_block *nb)
+{
+ return -EINVAL;
+}
+
+static inline int acpi_reconfig_notifier_unregister(struct notifier_block *nb)
+{
+ return -EINVAL;
+}
+
#endif /* CONFIG_ACPI */

#endif /*__ACPI_BUS_H__*/
--
1.9.1

2016-04-19 22:39:47

by Octavian Purdila

[permalink] [raw]
Subject: [RFC PATCH v2 03/10] acpi: fix enumeration (visited) flags for bus rescans

If the ACPI tables changes as a result of a dinamically loaded table and
a bus rescan is required the enumeration/visited flag are not
consistent.

I2C/SPI are not directly enumerated in acpi_bus_attach(), however the
visited flag is set. This makes it impossible to check if an ACPI device
has already been enumerated by the I2C and SPI subsystems. To fix this
issue we only set the visited flags if the device is not I2C or SPI.

With this change we also need to remove setting visited to false from
acpi_bus_attach(), otherwise if we rescan already enumerated I2C/SPI
devices we try to re-enumerate them.

Signed-off-by: Octavian Purdila <[email protected]>
---
drivers/acpi/scan.c | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 5f28cf7..7656ea4 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -1670,7 +1670,7 @@ static int acpi_check_spi_i2c_slave(struct acpi_resource *ares, void *data)
return -1;
}

-static void acpi_default_enumeration(struct acpi_device *device)
+static bool acpi_default_enumeration(struct acpi_device *device)
{
struct list_head resource_list;
bool is_spi_i2c_slave = false;
@@ -1685,6 +1685,7 @@ static void acpi_default_enumeration(struct acpi_device *device)
acpi_dev_free_resource_list(&resource_list);
if (!is_spi_i2c_slave)
acpi_create_platform_device(device);
+ return !is_spi_i2c_slave;
}

static const struct acpi_device_id generic_device_ids[] = {
@@ -1744,6 +1745,7 @@ static void acpi_bus_attach(struct acpi_device *device)
struct acpi_device *child;
acpi_handle ejd;
int ret;
+ bool enumerated = true;

if (ACPI_SUCCESS(acpi_bus_get_ejd(device->handle, &ejd)))
register_dock_dependent_device(device, ejd);
@@ -1766,7 +1768,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;
@@ -1778,9 +1780,10 @@ static void acpi_bus_attach(struct acpi_device *device)
return;

if (!ret && device->pnp.type.platform_id)
- acpi_default_enumeration(device);
+ enumerated = acpi_default_enumeration(device);
}
- device->flags.visited = true;
+ if (enumerated)
+ device->flags.visited = true;

ok:
list_for_each_entry(child, &device->children, node)
--
1.9.1

2016-04-19 22:39:57

by Octavian Purdila

[permalink] [raw]
Subject: [RFC PATCH v2 05/10] i2c: add support for ACPI reconfigure notifications

This patch adds supports for I2C device enumeration and removal via
ACPI reconfiguration notifications that are send as a result of an
ACPI table load or unload operation.

Signed-off-by: Octavian Purdila <[email protected]>
---
drivers/i2c/i2c-core.c | 170 ++++++++++++++++++++++++++++++++++++++-----------
1 file changed, 132 insertions(+), 38 deletions(-)

diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index 0f2f848..5a340df 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -107,12 +107,11 @@ struct acpi_i2c_lookup {
acpi_handle device_handle;
};

-static int acpi_i2c_find_address(struct acpi_resource *ares, void *data)
+static int acpi_i2c_fill_info(struct acpi_resource *ares, void *data)
{
struct acpi_i2c_lookup *lookup = data;
struct i2c_board_info *info = lookup->info;
struct acpi_resource_i2c_serialbus *sb;
- acpi_handle adapter_handle;
acpi_status status;

if (info->addr || ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS)
@@ -122,80 +121,102 @@ static int acpi_i2c_find_address(struct acpi_resource *ares, void *data)
if (sb->type != ACPI_RESOURCE_SERIAL_TYPE_I2C)
return 1;

- /*
- * Extract the ResourceSource and make sure that the handle matches
- * with the I2C adapter handle.
- */
status = acpi_get_handle(lookup->device_handle,
sb->resource_source.string_ptr,
- &adapter_handle);
- if (ACPI_SUCCESS(status) && adapter_handle == lookup->adapter_handle) {
- info->addr = sb->slave_address;
- if (sb->access_mode == ACPI_I2C_10BIT_MODE)
- info->flags |= I2C_CLIENT_TEN;
- }
+ &lookup->adapter_handle);
+ if (!ACPI_SUCCESS(status))
+ return 1;
+
+ info->addr = sb->slave_address;
+ if (sb->access_mode == ACPI_I2C_10BIT_MODE)
+ info->flags |= I2C_CLIENT_TEN;

return 1;
}

-static acpi_status acpi_i2c_add_device(acpi_handle handle, u32 level,
- void *data, void **return_value)
+static int acpi_i2c_get_info(struct acpi_device *adev,
+ struct i2c_board_info *info,
+ acpi_handle *adapter_handle)
{
- struct i2c_adapter *adapter = data;
struct list_head resource_list;
- struct acpi_i2c_lookup lookup;
struct resource_entry *entry;
- struct i2c_board_info info;
- struct acpi_device *adev;
+ struct acpi_i2c_lookup lookup;
int ret;

- if (acpi_bus_get_device(handle, &adev))
- return AE_OK;
- if (acpi_bus_get_status(adev) || !adev->status.present)
- return AE_OK;
+ if (acpi_bus_get_status(adev) || !adev->status.present ||
+ acpi_device_enumerated(adev))
+ return -EINVAL;

- memset(&info, 0, sizeof(info));
- info.fwnode = acpi_fwnode_handle(adev);
+ memset(info, 0, sizeof(*info));
+ info->fwnode = acpi_fwnode_handle(adev);

memset(&lookup, 0, sizeof(lookup));
- lookup.adapter_handle = ACPI_HANDLE(&adapter->dev);
- lookup.device_handle = handle;
- lookup.info = &info;
+ lookup.device_handle = acpi_device_handle(adev);
+ lookup.info = info;

- /*
- * Look up for I2cSerialBus resource with ResourceSource that
- * matches with this adapter.
- */
+ /* Look up for I2cSerialBus resource */
INIT_LIST_HEAD(&resource_list);
ret = acpi_dev_get_resources(adev, &resource_list,
- acpi_i2c_find_address, &lookup);
+ acpi_i2c_fill_info, &lookup);
acpi_dev_free_resource_list(&resource_list);

- if (ret < 0 || !info.addr)
- return AE_OK;
+ if (ret < 0 || !info->addr)
+ return -EINVAL;
+
+ *adapter_handle = lookup.adapter_handle;

/* Then fill IRQ number if any */
ret = acpi_dev_get_resources(adev, &resource_list, NULL, NULL);
if (ret < 0)
- return AE_OK;
+ return -EINVAL;

resource_list_for_each_entry(entry, &resource_list) {
if (resource_type(entry->res) == IORESOURCE_IRQ) {
- info.irq = entry->res->start;
+ info->irq = entry->res->start;
break;
}
}

acpi_dev_free_resource_list(&resource_list);

+ strlcpy(info->type, dev_name(&adev->dev), sizeof(info->type));
+
+ return 0;
+}
+
+static void acpi_i2c_register_device(struct i2c_adapter *adapter,
+ struct acpi_device *adev,
+ struct i2c_board_info *info)
+{
adev->power.flags.ignore_parent = true;
- strlcpy(info.type, dev_name(&adev->dev), sizeof(info.type));
- if (!i2c_new_device(adapter, &info)) {
+ adev->flags.visited = true;
+
+ if (!i2c_new_device(adapter, info)) {
adev->power.flags.ignore_parent = false;
dev_err(&adapter->dev,
"failed to add I2C device %s from ACPI\n",
dev_name(&adev->dev));
}
+}
+
+static acpi_status acpi_i2c_add_device(acpi_handle handle, u32 level,
+ void *data, void **return_value)
+{
+ struct i2c_adapter *adapter = data;
+ struct acpi_device *adev;
+ acpi_handle adapter_handle;
+ struct i2c_board_info info;
+
+ if (acpi_bus_get_device(handle, &adev))
+ return AE_OK;
+
+ if (acpi_i2c_get_info(adev, &info, &adapter_handle))
+ return AE_OK;
+
+ if (adapter_handle != ACPI_HANDLE(&adapter->dev))
+ return AE_OK;
+
+ acpi_i2c_register_device(adapter, adev, &info);

return AE_OK;
}
@@ -225,8 +246,77 @@ static void acpi_i2c_register_devices(struct i2c_adapter *adap)
dev_warn(&adap->dev, "failed to enumerate I2C slaves\n");
}

+static int acpi_i2c_match_adapter(struct device *dev, void *data)
+{
+ struct i2c_adapter *adapter = i2c_verify_adapter(dev);
+
+ if (!adapter)
+ return 0;
+
+ return ACPI_HANDLE(dev) == (acpi_handle)data;
+}
+
+static int acpi_i2c_match_device(struct device *dev, void *data)
+{
+ return ACPI_COMPANION(dev) == data;
+}
+
+static struct i2c_adapter *acpi_i2c_find_adapter_by_handle(acpi_handle handle)
+{
+ struct device *dev;
+
+ dev = bus_find_device(&i2c_bus_type, NULL, handle,
+ acpi_i2c_match_adapter);
+ return dev ? i2c_verify_adapter(dev) : NULL;
+}
+
+static struct i2c_client *acpi_i2c_find_client_by_adev(struct acpi_device *adev)
+{
+ struct device *dev;
+
+ dev = bus_find_device(&i2c_bus_type, NULL, adev, acpi_i2c_match_device);
+ return dev ? i2c_verify_client(dev) : NULL;
+}
+
+static int acpi_i2c_notify(struct notifier_block *nb, unsigned long value,
+ void *arg)
+{
+ struct acpi_device *adev = arg;
+ struct i2c_board_info info;
+ acpi_handle adapter_handle;
+ struct i2c_adapter *adapter;
+ struct i2c_client *client;
+
+ switch (value) {
+ case ACPI_RECONFIG_DEVICE_ADD:
+ if (acpi_i2c_get_info(adev, &info, &adapter_handle))
+ break;
+
+ adapter = acpi_i2c_find_adapter_by_handle(adapter_handle);
+ if (!adapter)
+ break;
+
+ acpi_i2c_register_device(adapter, adev, &info);
+ break;
+ case ACPI_RECONFIG_DEVICE_REMOVE:
+ client = acpi_i2c_find_client_by_adev(adev);
+ if (!client)
+ break;
+
+ i2c_unregister_device(client);
+ put_device(&client->dev);
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block i2c_acpi_notifier = {
+ .notifier_call = acpi_i2c_notify,
+};
#else /* CONFIG_ACPI */
static inline void acpi_i2c_register_devices(struct i2c_adapter *adap) { }
+extern struct notifier_block i2c_acpi_notifier;
#endif /* CONFIG_ACPI */

#ifdef CONFIG_ACPI_I2C_OPREGION
@@ -2121,6 +2211,8 @@ static int __init i2c_init(void)

if (IS_ENABLED(CONFIG_OF_DYNAMIC))
WARN_ON(of_reconfig_notifier_register(&i2c_of_notifier));
+ if (IS_ENABLED(CONFIG_ACPI))
+ WARN_ON(acpi_reconfig_notifier_register(&i2c_acpi_notifier));

return 0;

@@ -2136,6 +2228,8 @@ bus_err:

static void __exit i2c_exit(void)
{
+ if (IS_ENABLED(CONFIG_ACPI))
+ WARN_ON(acpi_reconfig_notifier_unregister(&i2c_acpi_notifier));
if (IS_ENABLED(CONFIG_OF_DYNAMIC))
WARN_ON(of_reconfig_notifier_unregister(&i2c_of_notifier));
i2c_del_driver(&dummy_driver);
--
1.9.1

2016-04-19 22:40:05

by Octavian Purdila

[permalink] [raw]
Subject: [RFC PATCH v2 07/10] efi: load SSTDs from EFI variables

This patch allows SSDTs to be loaded from EFI variables. It works by
specifying the EFI variable name containing the SSDT to be loaded. All
variables with the same name (regardless of the vendor GUID) will be
loaded.

Note that we can't use acpi_install_table and we must rely on the
dynamic ACPI table loading and bus re-scanning mechanisms. That is
because I2C/SPI controllers are initialized earlier then the EFI
subsystems and all I2C/SPI ACPI devices are enumerated when the
I2C/SPI controllers are initialized.

Signed-off-by: Octavian Purdila <[email protected]>
---
Documentation/acpi/ssdt-overlays.txt | 67 +++++++++++++++++++++
Documentation/kernel-parameters.txt | 7 +++
drivers/firmware/efi/efi.c | 109 +++++++++++++++++++++++++++++++++++
3 files changed, 183 insertions(+)

diff --git a/Documentation/acpi/ssdt-overlays.txt b/Documentation/acpi/ssdt-overlays.txt
index c328f6a..27f8108 100644
--- a/Documentation/acpi/ssdt-overlays.txt
+++ b/Documentation/acpi/ssdt-overlays.txt
@@ -88,3 +88,70 @@ cp ssdt.aml kernel/firmware/acpi
# on top:
find kernel | cpio -H newc --create > /boot/instrumented_initrd
cat /boot/initrd >>/boot/instrumented_initrd
+
+== Loading ACPI SSDTs from EFI variables ==
+
+This is the preferred method, when EFI is supported on the platform, because it
+allows a persistent, OS independent way of storing the user defined SSDTs. There
+is also work underway to implement EFI support for loading user defined SSDTs
+and using this method will make it easier to convert to the EFI loading
+mechanism when that will arrive.
+
+In order to load SSDTs from an EFI variable the efivar_ssdt kernel command line
+parameter can be used. The argument for the option is the variable name to
+use. If there are multiple variables with the same name but with different
+vendor GUIDs, all of them will be loaded.
+
+In order to store the AML code in an EFI variable the efivarfs filesystem can be
+used. It is enabled and mounted by default in /sys/firmware/efi/efivars in all
+recent distribution.
+
+Creating a new file in /sys/firmware/efi/efivars will automatically create a new
+EFI variable. Updating a file in /sys/firmware/efi/efivars will update the EFI
+variable. Please note that the file name needs to be specially formatted as
+"Name-GUID" and that the first 4 bytes in the file (little-endian format)
+represent the attributes of the EFI variable (see EFI_VARIABLE_MASK in
+include/linux/efi.h). Writing to the file must also be done with one write
+operation.
+
+For example, you can use the following bash script to create/update an EFI
+variable with the content from a given file:
+
+#!/bin/sh -e
+
+while ! [ -z "$1" ]; do
+ case "$1" in
+ "-f") filename="$2"; shift;;
+ "-g") guid="$2"; shift;;
+ *) name="$1";;
+ esac
+ shift
+done
+
+usage()
+{
+ echo "Syntax: ${0##*/} -f filename [ -g guid ] name"
+ exit 1
+}
+
+[ -n "$name" -a -f "$filename" ] || usage
+
+EFIVARFS="/sys/firmware/efi/efivars"
+
+[ -d "$EFIVARFS" ] || exit 2
+
+if stat -tf $EFIVARFS | grep -q -v de5e81e4; then
+ mount -t efivarfs none $EFIVARFS
+fi
+
+# try to pick up an existing GUID
+[ -n "$guid" ] || guid=$(find "$EFIVARFS" -name "$name-*" | head -n1 | cut -f2- -d-)
+
+# use a randomly generated GUID
+[ -n "$guid" ] || guid="$(cat /proc/sys/kernel/random/uuid)"
+
+# efivarfs expects all of the data in one write
+tmp=$(mktemp)
+/bin/echo -ne "\007\000\000\000" | cat - $filename > $tmp
+dd if=$tmp of="$EFIVARFS/$name-$guid" bs=$(stat -c %s $tmp)
+rm $tmp
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index ecc74fa..f678ad9 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -1164,6 +1164,13 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
Address Range Mirroring feature even if your box
doesn't support it.

+ efivar_ssdt= [EFI; X86] Name of an EFI variable that contains an SSDT
+ that is to be dynamically loaded by Linux. If there are
+ multiple variables with the same name but with different
+ vendor GUIDs, all of them will be loaded. See
+ Documentation/acpi/ssdt-overlays.txt for details.
+
+
eisa_irq_edge= [PARISC,HW]
See header of drivers/parisc/eisa.c.

diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
index 3a69ed5..78811f8 100644
--- a/drivers/firmware/efi/efi.c
+++ b/drivers/firmware/efi/efi.c
@@ -24,6 +24,8 @@
#include <linux/of_fdt.h>
#include <linux/io.h>
#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/acpi.h>

#include <asm/early_ioremap.h>

@@ -194,6 +196,110 @@ static void generic_ops_unregister(void)
efivars_unregister(&generic_efivars);
}

+#if IS_ENABLED(CONFIG_ACPI)
+#define EFIVAR_SSDT_NAME_MAX 16
+static char efivar_ssdt[EFIVAR_SSDT_NAME_MAX];
+static int __init efivar_ssdt_setup(char *str)
+{
+ if (strlen(str) < sizeof(efivar_ssdt))
+ memcpy(efivar_ssdt, str, strlen(str));
+ else
+ pr_warn("efivar_ssdt: name too long: %s\n", str);
+ return 0;
+}
+__setup("efivar_ssdt=", efivar_ssdt_setup);
+
+static LIST_HEAD(efivar_ssdts);
+
+static inline void pr_efivar_name(efi_char16_t *name16)
+{
+ char name[EFIVAR_SSDT_NAME_MAX];
+ int i;
+
+ for (i = 0; i < EFIVAR_SSDT_NAME_MAX - 1; i++)
+ name[i] = name16[i] & 0xFF;
+ name[i] = 0;
+ pr_cont("%s", name);
+}
+
+static __init int efivar_acpi_iter(efi_char16_t *name, efi_guid_t vendor,
+ unsigned long name_size, void *data)
+{
+ int i;
+ int str_len = name_size / sizeof(efi_char16_t);
+ struct efivar_entry *entry;
+
+ if (str_len != strlen(efivar_ssdt) + 1)
+ return 0;
+
+ for (i = 0; i < str_len; i++)
+ if ((name[i] & 0xFF) != efivar_ssdt[i])
+ return 0;
+
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+
+ memcpy(entry->var.VariableName, name, name_size);
+ memcpy(&entry->var.VendorGuid, &vendor, sizeof(efi_guid_t));
+
+ efivar_entry_add(entry, &efivar_ssdts);
+
+ return 0;
+}
+
+static __init int efivar_ssdt_load(void)
+{
+ struct efivar_entry *i;
+ int err;
+
+ err = efivar_init(efivar_acpi_iter, NULL, false, false,
+ &efivar_ssdts);
+ if (err) {
+ pr_err("%s: efivar_init failed: %d\n", __func__, err);
+ return err;
+ }
+
+ list_for_each_entry(i, &efivar_ssdts, list) {
+ void *data;
+ unsigned long size;
+
+ pr_info("loading SSDT from EFI variable ");
+ pr_efivar_name(i->var.VariableName); pr_cont("\n");
+
+ err = efivar_entry_size(i, &size);
+ if (err) {
+ pr_err("failed to get size\n");
+ continue;
+ }
+
+ data = kmalloc(size, GFP_KERNEL);
+ if (!data)
+ continue;
+
+ err = efivar_entry_get(i, NULL, &size, data);
+ if (err) {
+ pr_err("failed to get data\n");
+ kfree(data);
+ continue;
+ }
+
+ err = acpi_load_table(data);
+ if (err) {
+ pr_err("failed to load table: %d\n", err);
+ kfree(data);
+ continue;
+ }
+
+ add_taint(TAINT_OVERLAY_ACPI_TABLE, LOCKDEP_STILL_OK);
+ }
+
+ return 0;
+}
+#else
+static inline int efivar_ssdt_load(void) {}
+#endif
+
/*
* We register the efi subsystem with the firmware subsystem and the
* efivars subsystem with the efi subsystem, if the system was booted with
@@ -217,6 +323,9 @@ static int __init efisubsys_init(void)
if (error)
goto err_put;

+ if (efi_enabled(EFI_RUNTIME_SERVICES))
+ efivar_ssdt_load();
+
error = sysfs_create_group(efi_kobj, &efi_subsys_attr_group);
if (error) {
pr_err("efi: Sysfs attribute export failed with error %d.\n",
--
1.9.1

2016-04-19 22:40:11

by Octavian Purdila

[permalink] [raw]
Subject: [RFC PATCH v2 09/10] acpi: add support for loading SSDTs via configfs

Add support for acpi_user_table configfs items that allows the user to
load new tables. The data attributes contains the table data and once it
is filled from userspace the table is loaded and ACPI devices are
enumerated.

Signed-off-by: Octavian Purdila <[email protected]>
---
Documentation/ABI/testing/configfs-acpi | 16 +++++
Documentation/acpi/ssdt-overlays.txt | 14 +++++
drivers/acpi/configfs.c | 103 +++++++++++++++++++++++++++++++-
3 files changed, 132 insertions(+), 1 deletion(-)

diff --git a/Documentation/ABI/testing/configfs-acpi b/Documentation/ABI/testing/configfs-acpi
index 0c806aa..34a205e 100644
--- a/Documentation/ABI/testing/configfs-acpi
+++ b/Documentation/ABI/testing/configfs-acpi
@@ -5,3 +5,19 @@ Contact: [email protected]
Description:
This represents the ACPI subsystem entry point directory. It
contains sub-groups corresponding to ACPI configurable options.
+
+What: /config/acpi/table
+Date: April 2016
+KernelVersion: 4.6
+Description:
+
+ This group contains the configuration for user defined ACPI
+ tables. The attributes of a user define table are:
+
+ data - a binary write only attribute that the user can use to
+ fill in the ACPI aml definitions. Once the aml data is
+ written to this file and the file is closed the table
+ will be loaded and ACPI device will be enumerated. To
+ check if the operation is successful the user must check
+ the error code for close(). If the operation is
+ successful, subsequent writes to this attribute will fail.
diff --git a/Documentation/acpi/ssdt-overlays.txt b/Documentation/acpi/ssdt-overlays.txt
index 27f8108..f74202f 100644
--- a/Documentation/acpi/ssdt-overlays.txt
+++ b/Documentation/acpi/ssdt-overlays.txt
@@ -155,3 +155,17 @@ tmp=$(mktemp)
/bin/echo -ne "\007\000\000\000" | cat - $filename > $tmp
dd if=$tmp of="$EFIVARFS/$name-$guid" bs=$(stat -c %s $tmp)
rm $tmp
+
+== Loading ACPI SSDTs from configfs ==
+
+This option allows loading of user defined SSDTs from userspace via the configfs
+interface. The CONFIG_ACPI_CONFIGFS option must be select and configfs must be
+mounted. In the following examples, we assume that configfs has been mounted in
+/config.
+
+New tables can be loading by creating new directories in /config/acpi/table/ and
+writing the SSDT aml code in the data attribute:
+
+cd /config/acpi/table
+mkdir my_ssdt
+cat ~/ssdt.aml > my_ssdt/data
diff --git a/drivers/acpi/configfs.c b/drivers/acpi/configfs.c
index 96aa3d8..5efa8ca 100644
--- a/drivers/acpi/configfs.c
+++ b/drivers/acpi/configfs.c
@@ -13,6 +13,104 @@
#include <linux/configfs.h>
#include <linux/acpi.h>

+static struct config_group *acpi_table_group;
+
+struct acpi_user_table {
+ struct config_item cfg;
+ struct acpi_table_header *table;
+};
+
+static ssize_t acpi_table_data_write(struct config_item *cfg,
+ const void *data, size_t size)
+{
+ struct acpi_table_header *header = (struct acpi_table_header *)data;
+ struct acpi_user_table *table;
+ int ret;
+
+ table = container_of(cfg, struct acpi_user_table, cfg);
+
+ if (table->table) {
+ pr_err("ACPI configfs table: table already loaded\n");
+ return -EBUSY;
+ }
+
+ if (header->length != size) {
+ pr_err("ACPI configfs table: invalid table length\n");
+ return -EINVAL;
+ }
+
+ if (memcmp(header->signature, ACPI_SIG_SSDT, 4)) {
+ pr_err("ACPI configfs table: invalid table signature\n");
+ return -EINVAL;
+ }
+
+ table = container_of(cfg, struct acpi_user_table, cfg);
+
+ table->table = kmemdup(header, header->length, GFP_KERNEL);
+ if (!table->table)
+ return -ENOMEM;
+
+ ret = acpi_load_table(table->table);
+ if (ret) {
+ kfree(table->table);
+ table->table = NULL;
+ }
+
+ add_taint(TAINT_OVERLAY_ACPI_TABLE, LOCKDEP_STILL_OK);
+
+ return ret;
+}
+
+#define MAX_ACPI_TABLE_SIZE (128 * 1024)
+
+CONFIGFS_BIN_ATTR_WO(acpi_table_, data, NULL, MAX_ACPI_TABLE_SIZE);
+
+struct configfs_bin_attribute *acpi_table_bin_attrs[] = {
+ &acpi_table_attr_data,
+ NULL,
+};
+
+static struct config_item_type acpi_table_type = {
+ .ct_owner = THIS_MODULE,
+ .ct_bin_attrs = acpi_table_bin_attrs,
+};
+
+static struct config_item *acpi_table_make_item(struct config_group *group,
+ const char *name)
+{
+ struct acpi_user_table *table;
+
+ table = kzalloc(sizeof(*table), GFP_KERNEL);
+ if (!table)
+ return ERR_PTR(-ENOMEM);
+
+ config_item_init_type_name(&table->cfg, name, &acpi_table_type);
+ return &table->cfg;
+}
+
+struct configfs_group_operations acpi_table_group_ops = {
+ .make_item = acpi_table_make_item,
+};
+
+static struct config_item_type acpi_tables_type = {
+ .ct_owner = THIS_MODULE,
+ .ct_group_ops = &acpi_table_group_ops,
+};
+
+static struct config_item_type acpi_root_group_type = {
+ .ct_owner = THIS_MODULE,
+};
+
+static struct configfs_subsystem acpi_configfs = {
+ .su_group = {
+ .cg_item = {
+ .ci_namebuf = "acpi",
+ .ci_type = &acpi_root_group_type,
+ },
+ },
+ .su_mutex = __MUTEX_INITIALIZER(acpi_configfs.su_mutex),
+};
+
static int __init acpi_configfs_init(void)
{
int ret;
@@ -24,12 +122,15 @@ static int __init acpi_configfs_init(void)
if (ret)
return ret;

- return 0;
+ acpi_table_group = configfs_register_default_group(root, "table",
+ &acpi_tables_type);
+ return PTR_ERR_OR_ZERO(acpi_table_group);
}
module_init(acpi_configfs_init);

static void __exit acpi_configfs_exit(void)
{
+ configfs_unregister_default_group(acpi_table_group);
configfs_unregister_subsystem(&acpi_configfs);
}
module_exit(acpi_configfs_exit);
--
1.9.1

2016-04-19 22:40:38

by Octavian Purdila

[permalink] [raw]
Subject: [RFC PATCH v2 10/10] HACK: acpi: configfs: add unload_hanlde_path attribute for tables

Because there is no way to get a references to a table that we can use
to unload a table this patch adds an attribute allows the user to attach
a path to an ACPI handle to be used when unloading the table. The ACPI
handle must have been loaded with the table to which attribute is part
of, otherwise the unload operation will not be correct.

This patch should only be used to test table unloading and removal of
associated ACPI devices and should not be merged. Once the new table
loading/unloading APIs make it in ACPICA we can implement table
unloading properly.

Signed-off-by: Octavian Purdila <[email protected]>
---
drivers/acpi/configfs.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 58 insertions(+)

diff --git a/drivers/acpi/configfs.c b/drivers/acpi/configfs.c
index 5efa8ca..7bcdfbb 100644
--- a/drivers/acpi/configfs.c
+++ b/drivers/acpi/configfs.c
@@ -18,6 +18,7 @@ static struct config_group *acpi_table_group;
struct acpi_user_table {
struct config_item cfg;
struct acpi_table_header *table;
+ acpi_handle handle;
};

static ssize_t acpi_table_data_write(struct config_item *cfg,
@@ -65,14 +66,59 @@ static ssize_t acpi_table_data_write(struct config_item *cfg,

CONFIGFS_BIN_ATTR_WO(acpi_table_, data, NULL, MAX_ACPI_TABLE_SIZE);

+ssize_t acpi_table_unload_handle_path_show(struct config_item *item, char *page)
+{
+ struct acpi_buffer path = {ACPI_ALLOCATE_BUFFER, NULL};
+ struct acpi_user_table *table;
+ int ret;
+
+ table = container_of(item, struct acpi_user_table, cfg);
+
+ ret = acpi_get_name(table->handle, ACPI_FULL_PATHNAME, &path);
+ if (ret)
+ return ret;
+
+ ret = sprintf(page, "%s\n", (char *)path.pointer);
+ kfree(path.pointer);
+
+ return ret;
+}
+
+ssize_t acpi_table_unload_handle_path_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ struct acpi_user_table *table;
+ char *str;
+
+ table = container_of(item, struct acpi_user_table, cfg);
+
+ str = kmalloc(count + 1, GFP_KERNEL);
+ memcpy(str, page, count);
+ str[count] = 0;
+
+ acpi_get_handle(NULL, strim(str), &table->handle);
+
+ kfree(str);
+
+ return count;
+}
+
+CONFIGFS_ATTR(acpi_table_, unload_handle_path);
+
struct configfs_bin_attribute *acpi_table_bin_attrs[] = {
&acpi_table_attr_data,
NULL,
};

+struct configfs_attribute *acpi_table_attrs[] = {
+ &acpi_table_attr_unload_handle_path,
+ NULL,
+};
+
static struct config_item_type acpi_table_type = {
.ct_owner = THIS_MODULE,
.ct_bin_attrs = acpi_table_bin_attrs,
+ .ct_attrs = acpi_table_attrs,
};

static struct config_item *acpi_table_make_item(struct config_group *group,
@@ -88,8 +134,20 @@ static struct config_item *acpi_table_make_item(struct config_group *group,
return &table->cfg;
}

+static void acpi_table_drop_item(struct config_group *group,
+ struct config_item *item)
+{
+ struct acpi_user_table *table;
+
+ table = container_of(item, struct acpi_user_table, cfg);
+
+ if (table->handle)
+ acpi_unload_parent_table(table->handle);
+}
+
struct configfs_group_operations acpi_table_group_ops = {
.make_item = acpi_table_make_item,
+ .drop_item = acpi_table_drop_item,
};

static struct config_item_type acpi_tables_type = {
--
1.9.1

2016-04-19 22:41:03

by Octavian Purdila

[permalink] [raw]
Subject: [RFC PATCH v2 08/10] acpi: add support for configfs

Register the ACPI subsystem with configfs.

Signed-off-by: Octavian Purdila <[email protected]>
---
Documentation/ABI/testing/configfs-acpi | 7 ++++++
MAINTAINERS | 1 +
drivers/acpi/Kconfig | 9 ++++++++
drivers/acpi/Makefile | 1 +
drivers/acpi/configfs.c | 39 +++++++++++++++++++++++++++++++++
5 files changed, 57 insertions(+)
create mode 100644 Documentation/ABI/testing/configfs-acpi
create mode 100644 drivers/acpi/configfs.c

diff --git a/Documentation/ABI/testing/configfs-acpi b/Documentation/ABI/testing/configfs-acpi
new file mode 100644
index 0000000..0c806aa
--- /dev/null
+++ b/Documentation/ABI/testing/configfs-acpi
@@ -0,0 +1,7 @@
+What: /config/acpi
+Date: April 2016
+KernelVersion: 4.6
+Contact: [email protected]
+Description:
+ This represents the ACPI subsystem entry point directory. It
+ contains sub-groups corresponding to ACPI configurable options.
diff --git a/MAINTAINERS b/MAINTAINERS
index 1c32f8a..6695648 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -289,6 +289,7 @@ F: include/linux/acpi.h
F: include/acpi/
F: Documentation/acpi/
F: Documentation/ABI/testing/sysfs-bus-acpi
+F: Documentation/ABI/testing/configfs-acpi
F: drivers/pci/*acpi*
F: drivers/pci/*/*acpi*
F: drivers/pci/*/*/*acpi*
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 82b96ee..7d02816 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -521,4 +521,13 @@ config XPOWER_PMIC_OPREGION

endif

+config ACPI_CONFIGFS
+ tristate "ACPI configfs support"
+ select CONFIGFS_FS
+ default n
+ help
+ Select this option to enable support for ACPI configuration from
+ userspace. The configurable ACPI groups will be visible under
+ /config/acpi, assuming configfs is mounted under /config.
+
endif # ACPI
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index edeb2d1..319e63d 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -98,5 +98,6 @@ obj-$(CONFIG_ACPI_EXTLOG) += acpi_extlog.o
obj-$(CONFIG_PMIC_OPREGION) += pmic/intel_pmic.o
obj-$(CONFIG_CRC_PMIC_OPREGION) += pmic/intel_pmic_crc.o
obj-$(CONFIG_XPOWER_PMIC_OPREGION) += pmic/intel_pmic_xpower.o
+obj-$(CONFIG_ACPI_CONFIGFS) += configfs.o

video-objs += acpi_video.o video_detect.o
diff --git a/drivers/acpi/configfs.c b/drivers/acpi/configfs.c
new file mode 100644
index 0000000..96aa3d8
--- /dev/null
+++ b/drivers/acpi/configfs.c
@@ -0,0 +1,39 @@
+/*
+ * ACPI configfs support
+ *
+ * Copyright (c) 2015 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/configfs.h>
+#include <linux/acpi.h>
+
+static int __init acpi_configfs_init(void)
+{
+ int ret;
+ struct config_group *root = &acpi_configfs.su_group;
+
+ config_group_init(root);
+
+ ret = configfs_register_subsystem(&acpi_configfs);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+module_init(acpi_configfs_init);
+
+static void __exit acpi_configfs_exit(void)
+{
+ configfs_unregister_subsystem(&acpi_configfs);
+}
+module_exit(acpi_configfs_exit);
+
+MODULE_AUTHOR("Octavian Purdila <[email protected]>");
+MODULE_DESCRIPTION("ACPI configfs support");
+MODULE_LICENSE("GPL v2");
--
1.9.1

2016-04-19 22:41:34

by Octavian Purdila

[permalink] [raw]
Subject: [RFC PATCH v2 06/10] spi: add support for ACPI reconfigure notifications

This patch adds supports for SPI device enumeration and removal via
ACPI reconfiguration notifications that are send as a result of an
ACPI table load or unload operation.

Signed-off-by: Octavian Purdila <[email protected]>
---
drivers/spi/spi.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 87 insertions(+), 7 deletions(-)

diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index de2f2f9..d6c5a9c 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -1644,18 +1644,15 @@ static int acpi_spi_add_resource(struct acpi_resource *ares, void *data)
return 1;
}

-static acpi_status acpi_spi_add_device(acpi_handle handle, u32 level,
- void *data, void **return_value)
+static acpi_status acpi_register_spi_device(struct spi_master *master,
+ struct acpi_device *adev)
{
- struct spi_master *master = data;
struct list_head resource_list;
- struct acpi_device *adev;
struct spi_device *spi;
int ret;

- if (acpi_bus_get_device(handle, &adev))
- return AE_OK;
- if (acpi_bus_get_status(adev) || !adev->status.present)
+ if (acpi_bus_get_status(adev) || !adev->status.present ||
+ acpi_device_enumerated(adev))
return AE_OK;

spi = spi_alloc_device(master);
@@ -1681,6 +1678,8 @@ static acpi_status acpi_spi_add_device(acpi_handle handle, u32 level,
if (spi->irq < 0)
spi->irq = acpi_dev_gpio_irq_get(adev, 0);

+ adev->flags.visited = true;
+
adev->power.flags.ignore_parent = true;
strlcpy(spi->modalias, acpi_device_hid(adev), sizeof(spi->modalias));
if (spi_add_device(spi)) {
@@ -1693,6 +1692,18 @@ static acpi_status acpi_spi_add_device(acpi_handle handle, u32 level,
return AE_OK;
}

+static acpi_status acpi_spi_add_device(acpi_handle handle, u32 level,
+ void *data, void **return_value)
+{
+ struct spi_master *master = data;
+ struct acpi_device *adev;
+
+ if (acpi_bus_get_device(handle, &adev))
+ return AE_OK;
+
+ return acpi_register_spi_device(master, adev);
+}
+
static void acpi_register_spi_devices(struct spi_master *master)
{
acpi_status status;
@@ -3104,6 +3115,73 @@ static struct notifier_block spi_of_notifier = {
extern struct notifier_block spi_of_notifier;
#endif /* IS_ENABLED(CONFIG_OF_DYNAMIC) */

+#if IS_ENABLED(CONFIG_ACPI)
+static int spi_acpi_master_match(struct device *dev, const void *data)
+{
+ return ACPI_COMPANION(dev->parent) == data;
+}
+
+static int spi_acpi_device_match(struct device *dev, void *data)
+{
+ return ACPI_COMPANION(dev) == data;
+}
+
+static struct spi_master *acpi_spi_find_master_by_adev(struct acpi_device *adev)
+{
+ struct device *dev;
+
+ dev = class_find_device(&spi_master_class, NULL, adev,
+ spi_acpi_master_match);
+ if (!dev)
+ return NULL;
+
+ return container_of(dev, struct spi_master, dev);
+}
+
+static struct spi_device *acpi_spi_find_device_by_adev(struct acpi_device *adev)
+{
+ struct device *dev;
+
+ dev = bus_find_device(&spi_bus_type, NULL, adev, spi_acpi_device_match);
+
+ return dev ? to_spi_device(dev) : NULL;
+}
+
+static int acpi_spi_notify(struct notifier_block *nb, unsigned long value,
+ void *arg)
+{
+ struct acpi_device *adev = arg;
+ struct spi_master *master;
+ struct spi_device *spi;
+
+ switch (value) {
+ case ACPI_RECONFIG_DEVICE_ADD:
+ master = acpi_spi_find_master_by_adev(adev->parent);
+ if (!master)
+ break;
+
+ acpi_register_spi_device(master, adev);
+ put_device(&master->dev);
+ break;
+ case ACPI_RECONFIG_DEVICE_REMOVE:
+ spi = acpi_spi_find_device_by_adev(adev);
+ if (!spi)
+ break;
+
+ spi_unregister_device(spi);
+ put_device(&spi->dev);
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block spi_acpi_notifier = {
+ .notifier_call = acpi_spi_notify,
+};
+#else
+extern struct notifier_block spi_acpi_notifier;
+#endif
+
static int __init spi_init(void)
{
int status;
@@ -3124,6 +3202,8 @@ static int __init spi_init(void)

if (IS_ENABLED(CONFIG_OF_DYNAMIC))
WARN_ON(of_reconfig_notifier_register(&spi_of_notifier));
+ if (IS_ENABLED(CONFIG_ACPI))
+ WARN_ON(acpi_reconfig_notifier_register(&spi_acpi_notifier));

return 0;

--
1.9.1

2016-04-19 22:42:49

by Octavian Purdila

[permalink] [raw]
Subject: [RFC PATCH v2 02/10] acpi: decouple initrd table install from CONFIG_ACPI_INITRD_TABLE_OVERRIDE

The ACPI override and overlay functionality is different, with the
latter being more then a debug option. Allow loading of ACPI tables from
initrd even if CONFIG_ACPI_INITRD_TABLE_OVERRIDE is not selected.

The patch also switches to using TAINT_OVERLAY_ACPI_TABLE and adds
documentation about how to load SSDT tables from initrd.

Signed-off-by: Octavian Purdila <[email protected]>
---
Documentation/acpi/ssdt-overlays.txt | 26 ++++++++++++
arch/x86/kernel/setup.c | 2 +-
drivers/acpi/osl.c | 80 +++++++++++++++++++-----------------
include/linux/acpi.h | 8 +---
4 files changed, 71 insertions(+), 45 deletions(-)

diff --git a/Documentation/acpi/ssdt-overlays.txt b/Documentation/acpi/ssdt-overlays.txt
index 54ee74a..c328f6a 100644
--- a/Documentation/acpi/ssdt-overlays.txt
+++ b/Documentation/acpi/ssdt-overlays.txt
@@ -62,3 +62,29 @@ AML Output: minnowmax.aml - 165 bytes, 6 named objects, 1 executable opcodes

The resulting AML code can then be loaded by the kernel using one of the methods
below.
+
+== Loading ACPI SSDTs from initrd ==
+
+This option allows loading of user defined SSDTs from initrd and it is useful
+when the system does not support EFI or when there is not enough EFI storage.
+
+It works in a similar way with initrd based ACPI tables overrides: SSDT aml code
+must be placed in the first, uncompressed, initrd under the
+"kernel/firmware/acpi" path. Multiple files can be used and this will translate
+in loading multiple tables. Only SSDT and OEM tables are allowed.
+
+Here is an example:
+
+# Add the raw ACPI tables to an uncompressed cpio archive.
+# They must be put into a /kernel/firmware/acpi directory inside the
+# cpio archive.
+# The uncompressed cpio archive must be the first.
+# Other, typically compressed cpio archives, must be
+# concatenated on top of the uncompressed one.
+mkdir -p kernel/firmware/acpi
+cp ssdt.aml kernel/firmware/acpi
+
+# Create the uncompressed cpio archive and concatenate the original initrd
+# on top:
+find kernel | cpio -H newc --create > /boot/instrumented_initrd
+cat /boot/initrd >>/boot/instrumented_initrd
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
index 2367ae0..1908668 100644
--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -1139,7 +1139,7 @@ void __init setup_arch(char **cmdline_p)
reserve_initrd();

#if defined(CONFIG_ACPI) && defined(CONFIG_BLK_DEV_INITRD)
- acpi_initrd_override((void *)initrd_start, initrd_end - initrd_start);
+ acpi_table_initrd_init((void *)initrd_start, initrd_end - initrd_start);
#endif

vsmp_init();
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
index 814d5f8..dcc99b4 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -602,15 +602,24 @@ acpi_os_predefined_override(const struct acpi_predefined_names *init_val,
return AE_OK;
}

-static void acpi_table_taint(struct acpi_table_header *table)
+static void acpi_table_taint(struct acpi_table_header *table, bool override)
{
- pr_warn(PREFIX
- "Override [%4.4s-%8.8s], this is unsafe: tainting kernel\n",
- table->signature, table->oem_table_id);
- add_taint(TAINT_OVERRIDDEN_ACPI_TABLE, LOCKDEP_NOW_UNRELIABLE);
+ enum lockdep_ok lockdep_ok;
+ unsigned int taint;
+
+ if (override) {
+ pr_warn(PREFIX "Override [%4.4s-%8.8s], this is unsafe: tainting kernel\n",
+ table->signature, table->oem_table_id);
+ lockdep_ok = false;
+ taint = TAINT_OVERRIDDEN_ACPI_TABLE;
+ } else {
+ lockdep_ok = true;
+ taint = TAINT_OVERLAY_ACPI_TABLE;
+ }
+
+ add_taint(taint, lockdep_ok);
}

-#ifdef CONFIG_ACPI_INITRD_TABLE_OVERRIDE
#include <linux/earlycpio.h>
#include <linux/memblock.h>

@@ -642,13 +651,13 @@ static const char * const table_sigs[] = {

#define ACPI_HEADER_SIZE sizeof(struct acpi_table_header)

-#define ACPI_OVERRIDE_TABLES 64
-static struct cpio_data __initdata acpi_initrd_files[ACPI_OVERRIDE_TABLES];
-static DECLARE_BITMAP(acpi_initrd_installed, ACPI_OVERRIDE_TABLES);
+#define ACPI_INITRD_TABLES 64
+static struct cpio_data acpi_initrd_files[ACPI_INITRD_TABLES] __initdata;
+static DECLARE_BITMAP(acpi_initrd_installed, ACPI_INITRD_TABLES);

#define MAP_CHUNK_SIZE (NR_FIX_BTMAPS << PAGE_SHIFT)

-void __init acpi_initrd_override(void *data, size_t size)
+void __init acpi_table_initrd_init(void *data, size_t size)
{
int sig, no, table_nr = 0, total_offset = 0;
long offset = 0;
@@ -659,7 +668,7 @@ void __init acpi_initrd_override(void *data, size_t size)
if (data == NULL || size == 0)
return;

- for (no = 0; no < ACPI_OVERRIDE_TABLES; no++) {
+ for (no = 0; no < ACPI_INITRD_TABLES; no++) {
file = find_cpio_data(cpio_path, data, size, &offset);
if (!file.data)
break;
@@ -668,8 +677,8 @@ void __init acpi_initrd_override(void *data, size_t size)
size -= offset;

if (file.size < sizeof(struct acpi_table_header)) {
- pr_err("ACPI OVERRIDE: Table smaller than ACPI header [%s%s]\n",
- cpio_path, file.name);
+ pr_err(PREFIX "initrd: Table smaller than ACPI header [%s%s]\n",
+ cpio_path, file.name);
continue;
}

@@ -680,18 +689,18 @@ void __init acpi_initrd_override(void *data, size_t size)
break;

if (!table_sigs[sig]) {
- pr_err("ACPI OVERRIDE: Unknown signature [%s%s]\n",
- cpio_path, file.name);
+ pr_err(PREFIX "initrd: Unknown signature [%s%s]\n",
+ cpio_path, file.name);
continue;
}
if (file.size != table->length) {
- pr_err("ACPI OVERRIDE: File length does not match table length [%s%s]\n",
- cpio_path, file.name);
+ pr_err(PREFIX "initrd: File length does not match table length [%s%s]\n",
+ cpio_path, file.name);
continue;
}
if (acpi_table_checksum(file.data, table->length)) {
- pr_err("ACPI OVERRIDE: Bad table checksum [%s%s]\n",
- cpio_path, file.name);
+ pr_err(PREFIX "initrd: Bad table checksum [%s%s]\n",
+ cpio_path, file.name);
continue;
}

@@ -756,6 +765,7 @@ void __init acpi_initrd_override(void *data, size_t size)
}
}

+#ifdef CONFIG_ACPI_INITRD_TABLE_OVERRIDE
acpi_status
acpi_os_physical_table_override(struct acpi_table_header *existing_table,
acpi_physical_address *address, u32 *length)
@@ -792,7 +802,7 @@ acpi_os_physical_table_override(struct acpi_table_header *existing_table,

*length = table_length;
*address = acpi_tables_addr + table_offset;
- acpi_table_taint(existing_table);
+ acpi_table_taint(existing_table, true);
acpi_os_unmap_memory(table, ACPI_HEADER_SIZE);
set_bit(table_index, acpi_initrd_installed);
break;
@@ -803,6 +813,17 @@ next_table:
}
return AE_OK;
}
+#else
+acpi_status
+acpi_os_physical_table_override(struct acpi_table_header *existing_table,
+ acpi_physical_address *address,
+ u32 *table_length)
+{
+ *table_length = 0;
+ *address = 0;
+ return AE_OK;
+}
+#endif /* CONFIG_ACPI_INITRD_TABLE_OVERRIDE */

void __init acpi_initrd_initialize_tables(void)
{
@@ -833,7 +854,7 @@ void __init acpi_initrd_initialize_tables(void)
goto next_table;
}

- acpi_table_taint(table);
+ acpi_table_taint(table, false);
acpi_os_unmap_memory(table, ACPI_HEADER_SIZE);
acpi_install_table(acpi_tables_addr + table_offset, TRUE);
set_bit(table_index, acpi_initrd_installed);
@@ -842,21 +863,6 @@ next_table:
table_index++;
}
}
-#else
-acpi_status
-acpi_os_physical_table_override(struct acpi_table_header *existing_table,
- acpi_physical_address *address,
- u32 *table_length)
-{
- *table_length = 0;
- *address = 0;
- return AE_OK;
-}
-
-void __init acpi_initrd_initialize_tables(void)
-{
-}
-#endif /* CONFIG_ACPI_INITRD_TABLE_OVERRIDE */

acpi_status
acpi_os_table_override(struct acpi_table_header *existing_table,
@@ -872,7 +878,7 @@ acpi_os_table_override(struct acpi_table_header *existing_table,
*new_table = (struct acpi_table_header *)AmlCode;
#endif
if (*new_table != NULL)
- acpi_table_taint(existing_table);
+ acpi_table_taint(existing_table, true);
return AE_OK;
}

diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 06ed7e5..d4a3cb2 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -190,13 +190,7 @@ static inline int acpi_debugger_notify_command_complete(void)
}
#endif

-#ifdef CONFIG_ACPI_INITRD_TABLE_OVERRIDE
-void acpi_initrd_override(void *data, size_t size);
-#else
-static inline void acpi_initrd_override(void *data, size_t size)
-{
-}
-#endif
+void acpi_table_initrd_init(void *data, size_t size);

#define BAD_MADT_ENTRY(entry, end) ( \
(!entry) || (unsigned long)entry + sizeof(*entry) > end || \
--
1.9.1

2016-04-21 21:27:18

by Andy Shevchenko

[permalink] [raw]
Subject: Re: [RFC PATCH v2 05/10] i2c: add support for ACPI reconfigure notifications

On Wed, Apr 20, 2016 at 1:39 AM, Octavian Purdila
<[email protected]> wrote:
> This patch adds supports for I2C device enumeration and removal via
> ACPI reconfiguration notifications that are send as a result of an
> ACPI table load or unload operation.

>
> acpi_dev_free_resource_list(&resource_list);
>
> + strlcpy(info->type, dev_name(&adev->dev), sizeof(info->type));

strscpy() ?

> +static int acpi_i2c_match_adapter(struct device *dev, void *data)
> +{
> + struct i2c_adapter *adapter = i2c_verify_adapter(dev);
> +
> + if (!adapter)
> + return 0;
> +

> + return ACPI_HANDLE(dev) == (acpi_handle)data;

What is suppose to return? Hidden bool to integer conversion is not
best option I suppose.

> +}
> +
> +static int acpi_i2c_match_device(struct device *dev, void *data)
> +{
> + return ACPI_COMPANION(dev) == data;

Ditto.

> +}

--
With Best Regards,
Andy Shevchenko

2016-04-21 21:41:12

by Octavian Purdila

[permalink] [raw]
Subject: Re: [RFC PATCH v2 05/10] i2c: add support for ACPI reconfigure notifications

On Fri, Apr 22, 2016 at 12:27 AM, Andy Shevchenko
<[email protected]> wrote:
>

Hi Andy and thanks for the review!

> On Wed, Apr 20, 2016 at 1:39 AM, Octavian Purdila
> <[email protected]> wrote:
> > This patch adds supports for I2C device enumeration and removal via
> > ACPI reconfiguration notifications that are send as a result of an
> > ACPI table load or unload operation.
>
> >
> > acpi_dev_free_resource_list(&resource_list);
> >
> > + strlcpy(info->type, dev_name(&adev->dev), sizeof(info->type));
>
> strscpy() ?

That is the original code, I just moved it in a different function.
Its probably best if we use a separate patch for this, but is it worth
it?

>
> > +static int acpi_i2c_match_adapter(struct device *dev, void *data)
> > +{
> > + struct i2c_adapter *adapter = i2c_verify_adapter(dev);
> > +
> > + if (!adapter)
> > + return 0;
> > +
>
> > + return ACPI_HANDLE(dev) == (acpi_handle)data;
>
> What is suppose to return? Hidden bool to integer conversion is not
> best option I suppose.
>

per bus_find_device() :

* The callback should return 0 if the device doesn't match and
non-zero
* if it does. If the callback returns non-zero, this function will
* return to the caller and not iterate over any more devices.
*/
struct device *bus_find_device(struct bus_type *bus,
struct device *start, void *data,
int (*match)(struct device *dev, void *data))


> > +}
> > +
> > +static int acpi_i2c_match_device(struct device *dev, void *data)
> > +{
> > + return ACPI_COMPANION(dev) == data;
>
> Ditto.
>
> > +}
>
> --
> With Best Regards,
> Andy Shevchenko

2016-04-28 14:59:05

by Mika Westerberg

[permalink] [raw]
Subject: Re: [RFC PATCH v2 05/10] i2c: add support for ACPI reconfigure notifications

On Wed, Apr 20, 2016 at 01:39:03AM +0300, Octavian Purdila wrote:
> This patch adds supports for I2C device enumeration and removal via
> ACPI reconfiguration notifications that are send as a result of an
> ACPI table load or unload operation.
>
> Signed-off-by: Octavian Purdila <[email protected]>

Looks reasonable to me,

Reviewed-by: Mika Westerberg <[email protected]>

2016-04-28 15:00:46

by Mika Westerberg

[permalink] [raw]
Subject: Re: [RFC PATCH v2 06/10] spi: add support for ACPI reconfigure notifications

On Wed, Apr 20, 2016 at 01:39:04AM +0300, Octavian Purdila wrote:
> This patch adds supports for SPI device enumeration and removal via
> ACPI reconfiguration notifications that are send as a result of an
> ACPI table load or unload operation.
>
> Signed-off-by: Octavian Purdila <[email protected]>

Same here, pretty straightforward.

Reviewed-by: Mika Westerberg <[email protected]>

2016-04-28 17:42:41

by Mark Brown

[permalink] [raw]
Subject: Re: [RFC PATCH v2 06/10] spi: add support for ACPI reconfigure notifications

On Wed, Apr 20, 2016 at 01:39:04AM +0300, Octavian Purdila wrote:

> + switch (value) {
> + case ACPI_RECONFIG_DEVICE_ADD:
> + master = acpi_spi_find_master_by_adev(adev->parent);
> + if (!master)
> + break;
> +
> + acpi_register_spi_device(master, adev);
> + put_device(&master->dev);
> + break;
> + case ACPI_RECONFIG_DEVICE_REMOVE:
> + spi = acpi_spi_find_device_by_adev(adev);
> + if (!spi)
> + break;

There's more code here now than I remember but this all looks *really*
close to the DT code except for the OF_POPULATED flag that we set when
things are instantiated in DT. The duplication seems bad but the fact
that we're missing the flag worries me... do we have guarantees that
ACPI won't double register?


Attachments:
(No filename) (729.00 B)
signature.asc (473.00 B)
Download all attachments

2016-04-28 19:38:01

by Octavian Purdila

[permalink] [raw]
Subject: Re: [RFC PATCH v2 06/10] spi: add support for ACPI reconfigure notifications

On Thu, Apr 28, 2016 at 8:42 PM, Mark Brown <[email protected]> wrote:
> On Wed, Apr 20, 2016 at 01:39:04AM +0300, Octavian Purdila wrote:
>
>> + switch (value) {
>> + case ACPI_RECONFIG_DEVICE_ADD:
>> + master = acpi_spi_find_master_by_adev(adev->parent);
>> + if (!master)
>> + break;
>> +
>> + acpi_register_spi_device(master, adev);
>> + put_device(&master->dev);
>> + break;
>> + case ACPI_RECONFIG_DEVICE_REMOVE:
>> + spi = acpi_spi_find_device_by_adev(adev);
>> + if (!spi)
>> + break;
>
> There's more code here now than I remember but this all looks *really*
> close to the DT code except for the OF_POPULATED flag that we set when
> things are instantiated in DT. The duplication seems bad but the fact
> that we're missing the flag worries me... do we have guarantees that
> ACPI won't double register?

We use the adev->flags.visited to check when a device has been already
enumerated, and we skip registering a new SPI slave in that case.