From patchwork Fri Jul 30 15:47:04 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Renninger X-Patchwork-Id: 115539 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter.kernel.org (8.14.4/8.14.3) with ESMTP id o6UFn6vr008209 for ; Fri, 30 Jul 2010 15:49:06 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754976Ab0G3Pr3 (ORCPT ); Fri, 30 Jul 2010 11:47:29 -0400 Received: from cantor.suse.de ([195.135.220.2]:37657 "EHLO mx1.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754953Ab0G3Pr2 (ORCPT ); Fri, 30 Jul 2010 11:47:28 -0400 Received: from relay2.suse.de (charybdis-ext.suse.de [195.135.221.2]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.suse.de (Postfix) with ESMTP id 5153290847; Fri, 30 Jul 2010 17:47:26 +0200 (CEST) From: Thomas Renninger To: mjg59@srcf.ucam.org Cc: Thomas Renninger , platform-driver-x86@vger.kernel.org, linux-acpi@vger.kernel.org, astarikovskiy@suse.de, akpm@linux-foundation.org Subject: [PATCH] acpi ec_sys: Export fields of all regions from the EC to debugfs readable Date: Fri, 30 Jul 2010 17:47:04 +0200 Message-Id: <1280504824-8134-1-git-send-email-trenn@suse.de> X-Mailer: git-send-email 1.6.3 Sender: linux-acpi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-acpi@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter.kernel.org [140.211.167.41]); Fri, 30 Jul 2010 15:49:06 +0000 (UTC) diff --git a/Documentation/ABI/testing/debugfs-ec b/Documentation/ABI/testing/debugfs-ec index 6546115..799ffd5 100644 --- a/Documentation/ABI/testing/debugfs-ec +++ b/Documentation/ABI/testing/debugfs-ec @@ -1,14 +1,18 @@ -What: /sys/kernel/debug/ec/*/{gpe,use_global_lock,io} +What: /sys/kernel/debug/ec/*/{gpe,use_global_lock,io,field/*} Date: July 2010 Contact: Thomas Renninger Description: +files: gpe, use_global_lock +--------------------------- General information like which GPE is assigned to the EC and whether the global lock should get used. -Knowing the EC GPE one can watch the amount of HW events related to +Knowing the EC GPE, one can watch the amount of HW events related to the EC here (XY -> GPE number from /sys/kernel/debug/ec/*/gpe): /sys/firmware/acpi/interrupts/gpeXY +file: io +-------- The io file is binary and a userspace tool located here: ftp://ftp.suse.com/pub/people/trenn/sources/ec/ should get used to read out the 256 Embedded Controller registers @@ -18,3 +22,28 @@ CAUTION: Do not write to the Embedded Controller if you don't know what you are doing! Rebooting afterwards also is a good idea. This can influence the way your machine is cooled and fans may not get switched on again after you did a wrong write. + +files: fields/* +--------------- + +Shows EC register fields as defined in the EmbeddedController object of the DSDT +Reads are done through the ACPI layer and may get accessed differently than +reading blank bytes through the io file (e.g. with burst mode enabled). +People can read the DSDT.dsl to map register names to EC fields, but with the +names displayed this should not be necessary anymore. + +Typical EC region definition: + OperationRegion (ECRM, EmbeddedControl, 0x00, 0xFF) + Field (ECRM, ByteAcc, NoLock, Preserve) + { + S0PW, 32, + S0CT, 32, + Offset (0x80), + FNSW, 1, + ... + } + +Reading /sys/../ec/*/fields/SOPW will read the first 4 bytes of the EC and +display it. +Reading ../FNSW will read one byte at offset 0x80 and mask out 7 bits and +return a bit value. diff --git a/drivers/acpi/ec_sys.c b/drivers/acpi/ec_sys.c index 0e869b3..768119d 100644 --- a/drivers/acpi/ec_sys.c +++ b/drivers/acpi/ec_sys.c @@ -6,6 +6,13 @@ * Thomas Renninger * * This work is licensed under the terms of the GNU GPL, version 2. + * + * Ideally this would not be a separate driver, but hook into the + * .add function of the ec.c driver. + * But the ec.c driver must be compiled in and it's essential that + * this debug module can be loaded at runtime. + * The driver relies on the fact that an ACPI EC object can + * never vanish at runtime. */ #include @@ -24,15 +31,27 @@ MODULE_PARM_DESC(write_support, "Dangerous, reboot and removal of battery may " #define EC_SPACE_SIZE 256 +/* Only allow one user to access any EC io/field file at a time */ +static DEFINE_MUTEX(ec_fs_lock); + struct sysdev_class acpi_ec_sysdev_class = { .name = "ec", }; static struct dentry *acpi_ec_debugfs_dir; -static int acpi_ec_open_io(struct inode *i, struct file *f) +static int acpi_ec_open(struct inode *i, struct file *f) { f->private_data = i->i_private; + if (mutex_trylock(&ec_fs_lock)) + return 0; + else + return -EBUSY; +} + +static int acpi_ec_release(struct inode *inode, struct file *file) +{ + mutex_unlock(&ec_fs_lock); return 0; } @@ -96,18 +115,109 @@ static ssize_t acpi_ec_write_io(struct file *f, const char __user *buf, return count; } +#ifdef ACPICA_CAN_DO_FIELD_WRITES +/* ACPICA currently does not provide a function to write to field + objects */ +static ssize_t acpi_ec_write_field(struct file *f, const char __user *buf, + size_t count, loff_t *off) +{ + struct acpi_ec *ec = f->private_data; + char name[5]; + union acpi_object arg0 = { .type = ACPI_TYPE_INTEGER }; + struct acpi_object_list arg_list = { 1, &arg0 }; + acpi_status status; + + if (*off > 0) + return -EINVAL; + + strncpy(name, f->f_dentry->d_name.name, 5); + + if (strict_strtoll(buf, 0, &arg0.integer.value)) + return -EINVAL; + + /* This does not work, only real methods can be evaluted + with an arguement */ + status = acpi_evaluate_object(ec->handle, name, &arg_list, NULL); + if (ACPI_FAILURE(status)) + return -EIO; + *off = count; + return count; +} +#endif + +static ssize_t acpi_ec_read_field(struct file *f, char __user *buf, + size_t count, loff_t *off) +{ + struct acpi_ec *ec = f->private_data; + char name[5]; + unsigned long long value; + acpi_status status; + + if (*off > 0) + return 0; + strncpy(name, f->f_dentry->d_name.name, 5); + + status = acpi_evaluate_integer(ec->handle, name, NULL, &value); + if (ACPI_FAILURE(status)) + return -EIO; + + count = *off = sprintf(buf, "0x%llx\n", value); + if (!*off) + return -EINVAL; + return count; +} + static struct file_operations acpi_ec_io_ops = { - .owner = THIS_MODULE, - .open = acpi_ec_open_io, - .read = acpi_ec_read_io, - .write = acpi_ec_write_io, + .owner = THIS_MODULE, + .open = acpi_ec_open, + .release = acpi_ec_release, + .read = acpi_ec_read_io, +}; + +static struct file_operations acpi_ec_field_ops = { + .owner = THIS_MODULE, + .open = acpi_ec_open, + .release = acpi_ec_release, + .llseek = no_llseek, + .read = acpi_ec_read_field, }; +static acpi_status acpi_ec_find_fields(acpi_handle handle, u32 level, + void *context, void **return_value) +{ + char node_name[5]; + struct acpi_buffer buffer = { sizeof(node_name), node_name }; + struct acpi_ec *ec = context; + acpi_status status; + mode_t mode = 0400; + +#ifdef ACPICA_CAN_DO_FIELD_WRITES + if (write_support) { + mode = 0600; + acpi_ec_field_ops.write = acpi_ec_write_field; + } +#endif + + status = acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer); + if (ACPI_SUCCESS(status)) { + if (!debugfs_create_file(node_name, mode, ec->field_dir, + ec, &acpi_ec_field_ops)) + return AE_ERROR; + } + return AE_OK; +} + int acpi_ec_add_debugfs(struct acpi_ec *ec, unsigned int ec_device_count) { - struct dentry *dev_dir; + struct dentry *dev_dir, *field_dir; char name[64]; mode_t mode = 0400; + acpi_status status; + + if (!write_support) { + mode = 0600; + acpi_ec_io_ops.write = acpi_ec_write_io; + } if (ec_device_count == 0) { acpi_ec_debugfs_dir = debugfs_create_dir("ec", NULL); @@ -129,13 +239,26 @@ int acpi_ec_add_debugfs(struct acpi_ec *ec, unsigned int ec_device_count) (u32 *)&first_ec->global_lock)) goto error; - if (write_support) - mode = 0600; if (!debugfs_create_file("io", mode, dev_dir, ec, &acpi_ec_io_ops)) goto error; + /* + * Find and register all fields. For now we do not differ + * EmbeddedController operation region fields because acpica does not + * allow us to do that. So some fields may result in system, io port,.. + * and not EC accesses. + */ + field_dir = debugfs_create_dir("fields", dev_dir); + if (!field_dir) + goto error; + ec->field_dir = field_dir; - return 0; + status = acpi_walk_namespace(ACPI_TYPE_LOCAL_REGION_FIELD, ec->handle, + 1, acpi_ec_find_fields, NULL, ec, + NULL); + if (ACPI_FAILURE(status)) + goto error; + return 0; error: debugfs_remove_recursive(acpi_ec_debugfs_dir); return -ENOMEM; diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 8ae2726..14d1bab 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -64,6 +64,9 @@ struct acpi_ec { struct transaction *curr; spinlock_t curr_lock; struct sys_device sysdev; +#if defined CONFIG_ACPI_EC_DEBUGFS || defined CONFIG_ACPI_EC_DEBUGFS_MODULE + struct dentry *field_dir; +#endif }; extern struct acpi_ec *first_ec;