ACPICA: Update to GPIO region handler interface.
authorBob Moore <Robert.Moore@intel.com>
Tue, 23 Sep 2014 02:35:47 +0000 (10:35 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 5 Oct 2014 21:54:11 +0000 (14:54 -0700)
commit 75ec6e55f1384548311a13ce4fcb39c516053314 upstream.

Changes to correct several GPIO issues:

1) The update_rule in a GPIO field definition is now ignored;
a read-modify-write operation is never performed for GPIO fields.
(Internally, this means that the field assembly/disassembly
code is completely bypassed for GPIO.)

2) The Address parameter passed to a GPIO region handler is
now the bit offset of the field from a previous Connection()
operator. Thus, it becomes a "Pin Number Index" into the
Connection() resource descriptor.

3) The bit_width parameter passed to a GPIO region handler is
now the exact bit width of the GPIO field. Thus, it can be
interpreted as "number of pins".

Overall, we can now say that the region handler interface
to GPIO handlers is a raw "bit/pin" addressed interface, not
a byte-addressed interface like the system_memory handler interface.

Signed-off-by: Bob Moore <robert.moore@intel.com>
Signed-off-by: Lv Zheng <lv.zheng@intel.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/acpi/acpica/aclocal.h
drivers/acpi/acpica/acobject.h
drivers/acpi/acpica/dsfield.c
drivers/acpi/acpica/evregion.c
drivers/acpi/acpica/exfield.c
drivers/acpi/acpica/exprep.c

index d5bfbd331bfdda124d92a195b3418ae34cfbc2af..95896886fc5ab8648bd705c157d0beaae9d32a6e 100644 (file)
@@ -254,6 +254,7 @@ struct acpi_create_field_info {
        u32 field_bit_position;
        u32 field_bit_length;
        u16 resource_length;
+       u16 pin_number_index;
        u8 field_flags;
        u8 attribute;
        u8 field_type;
index cc7ab6dd724e6234a835d0321c8cb4b6106796f4..a47cc78ffd4f0f9337f892b8b063312a1b791a69 100644 (file)
@@ -263,6 +263,7 @@ struct acpi_object_region_field {
        ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_FIELD_INFO u16 resource_length;
        union acpi_operand_object *region_obj;  /* Containing op_region object */
        u8 *resource_buffer;    /* resource_template for serial regions/fields */
+       u16 pin_number_index;   /* Index relative to previous Connection/Template */
 };
 
 struct acpi_object_bank_field {
index feadeed1012dc2256c1596bb79e7ff6190e6cb41..e651d4ec7c4ccc99a052b171635de74605c3ad70 100644 (file)
@@ -360,6 +360,7 @@ acpi_ds_get_field_names(struct acpi_create_field_info *info,
                         */
                        info->resource_buffer = NULL;
                        info->connection_node = NULL;
+                       info->pin_number_index = 0;
 
                        /*
                         * A Connection() is either an actual resource descriptor (buffer)
@@ -437,6 +438,7 @@ acpi_ds_get_field_names(struct acpi_create_field_info *info,
                        }
 
                        info->field_bit_position += info->field_bit_length;
+                       info->pin_number_index++;       /* Index relative to previous Connection() */
                        break;
 
                default:
index 6555e350fc1fe21087cea7e219a9c4b170d70e25..8fab9262d98afc68e07c6fdf7794850b2ebd20d6 100644 (file)
@@ -141,6 +141,7 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj,
        union acpi_operand_object *region_obj2;
        void *region_context = NULL;
        struct acpi_connection_info *context;
+       acpi_physical_address address;
 
        ACPI_FUNCTION_TRACE(ev_address_space_dispatch);
 
@@ -235,25 +236,23 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj,
        /* We have everything we need, we can invoke the address space handler */
 
        handler = handler_desc->address_space.handler;
-
-       ACPI_DEBUG_PRINT((ACPI_DB_OPREGION,
-                         "Handler %p (@%p) Address %8.8X%8.8X [%s]\n",
-                         &region_obj->region.handler->address_space, handler,
-                         ACPI_FORMAT_NATIVE_UINT(region_obj->region.address +
-                                                 region_offset),
-                         acpi_ut_get_region_name(region_obj->region.
-                                                 space_id)));
+       address = (region_obj->region.address + region_offset);
 
        /*
         * Special handling for generic_serial_bus and general_purpose_io:
         * There are three extra parameters that must be passed to the
         * handler via the context:
-        *   1) Connection buffer, a resource template from Connection() op.
-        *   2) Length of the above buffer.
-        *   3) Actual access length from the access_as() op.
+        *   1) Connection buffer, a resource template from Connection() op
+        *   2) Length of the above buffer
+        *   3) Actual access length from the access_as() op
+        *
+        * In addition, for general_purpose_io, the Address and bit_width fields
+        * are defined as follows:
+        *   1) Address is the pin number index of the field (bit offset from
+        *      the previous Connection)
+        *   2) bit_width is the actual bit length of the field (number of pins)
         */
-       if (((region_obj->region.space_id == ACPI_ADR_SPACE_GSBUS) ||
-            (region_obj->region.space_id == ACPI_ADR_SPACE_GPIO)) &&
+       if ((region_obj->region.space_id == ACPI_ADR_SPACE_GSBUS) &&
            context && field_obj) {
 
                /* Get the Connection (resource_template) buffer */
@@ -262,6 +261,24 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj,
                context->length = field_obj->field.resource_length;
                context->access_length = field_obj->field.access_length;
        }
+       if ((region_obj->region.space_id == ACPI_ADR_SPACE_GPIO) &&
+           context && field_obj) {
+
+               /* Get the Connection (resource_template) buffer */
+
+               context->connection = field_obj->field.resource_buffer;
+               context->length = field_obj->field.resource_length;
+               context->access_length = field_obj->field.access_length;
+               address = field_obj->field.pin_number_index;
+               bit_width = field_obj->field.bit_length;
+       }
+
+       ACPI_DEBUG_PRINT((ACPI_DB_OPREGION,
+                         "Handler %p (@%p) Address %8.8X%8.8X [%s]\n",
+                         &region_obj->region.handler->address_space, handler,
+                         ACPI_FORMAT_NATIVE_UINT(address),
+                         acpi_ut_get_region_name(region_obj->region.
+                                                 space_id)));
 
        if (!(handler_desc->address_space.handler_flags &
              ACPI_ADDR_HANDLER_DEFAULT_INSTALLED)) {
@@ -275,9 +292,7 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj,
 
        /* Call the handler */
 
-       status = handler(function,
-                        (region_obj->region.address + region_offset),
-                        bit_width, value, context,
+       status = handler(function, address, bit_width, value, context,
                         region_obj2->extra.region_context);
 
        if (ACPI_FAILURE(status)) {
index 7d4bae71e8c62da6399bc6af7e0f3fd132efa574..0108d59665abddb0edb55090b399675fcf663266 100644 (file)
@@ -178,6 +178,37 @@ acpi_ex_read_data_from_field(struct acpi_walk_state *walk_state,
                buffer = &buffer_desc->integer.value;
        }
 
+       if ((obj_desc->common.type == ACPI_TYPE_LOCAL_REGION_FIELD) &&
+           (obj_desc->field.region_obj->region.space_id ==
+            ACPI_ADR_SPACE_GPIO)) {
+               /*
+                * For GPIO (general_purpose_io), the Address will be the bit offset
+                * from the previous Connection() operator, making it effectively a
+                * pin number index. The bit_length is the length of the field, which
+                * is thus the number of pins.
+                */
+               ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
+                                 "GPIO FieldRead [FROM]:  Pin %u Bits %u\n",
+                                 obj_desc->field.pin_number_index,
+                                 obj_desc->field.bit_length));
+
+               /* Lock entire transaction if requested */
+
+               acpi_ex_acquire_global_lock(obj_desc->common_field.field_flags);
+
+               /* Perform the write */
+
+               status = acpi_ex_access_region(obj_desc, 0,
+                                              (u64 *)buffer, ACPI_READ);
+               acpi_ex_release_global_lock(obj_desc->common_field.field_flags);
+               if (ACPI_FAILURE(status)) {
+                       acpi_ut_remove_reference(buffer_desc);
+               } else {
+                       *ret_buffer_desc = buffer_desc;
+               }
+               return_ACPI_STATUS(status);
+       }
+
        ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
                          "FieldRead [TO]:   Obj %p, Type %X, Buf %p, ByteLen %X\n",
                          obj_desc, obj_desc->common.type, buffer,
@@ -325,6 +356,42 @@ acpi_ex_write_data_to_field(union acpi_operand_object *source_desc,
 
                *result_desc = buffer_desc;
                return_ACPI_STATUS(status);
+       } else if ((obj_desc->common.type == ACPI_TYPE_LOCAL_REGION_FIELD) &&
+                  (obj_desc->field.region_obj->region.space_id ==
+                   ACPI_ADR_SPACE_GPIO)) {
+               /*
+                * For GPIO (general_purpose_io), we will bypass the entire field
+                * mechanism and handoff the bit address and bit width directly to
+                * the handler. The Address will be the bit offset
+                * from the previous Connection() operator, making it effectively a
+                * pin number index. The bit_length is the length of the field, which
+                * is thus the number of pins.
+                */
+               if (source_desc->common.type != ACPI_TYPE_INTEGER) {
+                       return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
+               }
+
+               ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
+                                 "GPIO FieldWrite [FROM]: (%s:%X), Val %.8X  [TO]:  Pin %u Bits %u\n",
+                                 acpi_ut_get_type_name(source_desc->common.
+                                                       type),
+                                 source_desc->common.type,
+                                 (u32)source_desc->integer.value,
+                                 obj_desc->field.pin_number_index,
+                                 obj_desc->field.bit_length));
+
+               buffer = &source_desc->integer.value;
+
+               /* Lock entire transaction if requested */
+
+               acpi_ex_acquire_global_lock(obj_desc->common_field.field_flags);
+
+               /* Perform the write */
+
+               status = acpi_ex_access_region(obj_desc, 0,
+                                              (u64 *)buffer, ACPI_WRITE);
+               acpi_ex_release_global_lock(obj_desc->common_field.field_flags);
+               return_ACPI_STATUS(status);
        }
 
        /* Get a pointer to the data to be written */
index 6b728aef2dcab54804d11509e23660b06e11d21e..df212fe4cf6c1374fc777073879aa1df3749c166 100644 (file)
@@ -479,6 +479,8 @@ acpi_status acpi_ex_prep_field_value(struct acpi_create_field_info *info)
                        obj_desc->field.resource_length = info->resource_length;
                }
 
+               obj_desc->field.pin_number_index = info->pin_number_index;
+
                /* Allow full data read from EC address space */
 
                if ((obj_desc->field.region_obj->region.space_id ==