Commit 75ec6e55 authored by Bob Moore's avatar Bob Moore Committed by Rafael J. Wysocki

ACPICA: Update to GPIO region handler interface.

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: default avatarBob Moore <robert.moore@intel.com>
Signed-off-by: default avatarLv Zheng <lv.zheng@intel.com>
Cc: 3.15+ <stable@vger.kernel.org> # 3.15+
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent 0f33be00
...@@ -254,6 +254,7 @@ struct acpi_create_field_info { ...@@ -254,6 +254,7 @@ struct acpi_create_field_info {
u32 field_bit_position; u32 field_bit_position;
u32 field_bit_length; u32 field_bit_length;
u16 resource_length; u16 resource_length;
u16 pin_number_index;
u8 field_flags; u8 field_flags;
u8 attribute; u8 attribute;
u8 field_type; u8 field_type;
......
...@@ -264,6 +264,7 @@ struct acpi_object_region_field { ...@@ -264,6 +264,7 @@ struct acpi_object_region_field {
ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_FIELD_INFO u16 resource_length; ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_FIELD_INFO u16 resource_length;
union acpi_operand_object *region_obj; /* Containing op_region object */ union acpi_operand_object *region_obj; /* Containing op_region object */
u8 *resource_buffer; /* resource_template for serial regions/fields */ u8 *resource_buffer; /* resource_template for serial regions/fields */
u16 pin_number_index; /* Index relative to previous Connection/Template */
}; };
struct acpi_object_bank_field { struct acpi_object_bank_field {
......
...@@ -360,6 +360,7 @@ acpi_ds_get_field_names(struct acpi_create_field_info *info, ...@@ -360,6 +360,7 @@ acpi_ds_get_field_names(struct acpi_create_field_info *info,
*/ */
info->resource_buffer = NULL; info->resource_buffer = NULL;
info->connection_node = NULL; info->connection_node = NULL;
info->pin_number_index = 0;
/* /*
* A Connection() is either an actual resource descriptor (buffer) * A Connection() is either an actual resource descriptor (buffer)
...@@ -437,6 +438,7 @@ acpi_ds_get_field_names(struct acpi_create_field_info *info, ...@@ -437,6 +438,7 @@ acpi_ds_get_field_names(struct acpi_create_field_info *info,
} }
info->field_bit_position += info->field_bit_length; info->field_bit_position += info->field_bit_length;
info->pin_number_index++; /* Index relative to previous Connection() */
break; break;
default: default:
......
...@@ -142,6 +142,7 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj, ...@@ -142,6 +142,7 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj,
union acpi_operand_object *region_obj2; union acpi_operand_object *region_obj2;
void *region_context = NULL; void *region_context = NULL;
struct acpi_connection_info *context; struct acpi_connection_info *context;
acpi_physical_address address;
ACPI_FUNCTION_TRACE(ev_address_space_dispatch); ACPI_FUNCTION_TRACE(ev_address_space_dispatch);
...@@ -231,25 +232,23 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj, ...@@ -231,25 +232,23 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj,
/* We have everything we need, we can invoke the address space handler */ /* We have everything we need, we can invoke the address space handler */
handler = handler_desc->address_space.handler; handler = handler_desc->address_space.handler;
address = (region_obj->region.address + region_offset);
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)));
/* /*
* Special handling for generic_serial_bus and general_purpose_io: * Special handling for generic_serial_bus and general_purpose_io:
* There are three extra parameters that must be passed to the * There are three extra parameters that must be passed to the
* handler via the context: * handler via the context:
* 1) Connection buffer, a resource template from Connection() op. * 1) Connection buffer, a resource template from Connection() op
* 2) Length of the above buffer. * 2) Length of the above buffer
* 3) Actual access length from the access_as() op. * 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) || if ((region_obj->region.space_id == ACPI_ADR_SPACE_GSBUS) &&
(region_obj->region.space_id == ACPI_ADR_SPACE_GPIO)) &&
context && field_obj) { context && field_obj) {
/* Get the Connection (resource_template) buffer */ /* Get the Connection (resource_template) buffer */
...@@ -258,6 +257,24 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj, ...@@ -258,6 +257,24 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj,
context->length = field_obj->field.resource_length; context->length = field_obj->field.resource_length;
context->access_length = field_obj->field.access_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 & if (!(handler_desc->address_space.handler_flags &
ACPI_ADDR_HANDLER_DEFAULT_INSTALLED)) { ACPI_ADDR_HANDLER_DEFAULT_INSTALLED)) {
...@@ -271,9 +288,7 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj, ...@@ -271,9 +288,7 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj,
/* Call the handler */ /* Call the handler */
status = handler(function, status = handler(function, address, bit_width, value, context,
(region_obj->region.address + region_offset),
bit_width, value, context,
region_obj2->extra.region_context); region_obj2->extra.region_context);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
......
...@@ -253,6 +253,37 @@ acpi_ex_read_data_from_field(struct acpi_walk_state * walk_state, ...@@ -253,6 +253,37 @@ acpi_ex_read_data_from_field(struct acpi_walk_state * walk_state,
buffer = &buffer_desc->integer.value; 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, ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
"FieldRead [TO]: Obj %p, Type %X, Buf %p, ByteLen %X\n", "FieldRead [TO]: Obj %p, Type %X, Buf %p, ByteLen %X\n",
obj_desc, obj_desc->common.type, buffer, obj_desc, obj_desc->common.type, buffer,
...@@ -413,6 +444,42 @@ acpi_ex_write_data_to_field(union acpi_operand_object *source_desc, ...@@ -413,6 +444,42 @@ acpi_ex_write_data_to_field(union acpi_operand_object *source_desc,
*result_desc = buffer_desc; *result_desc = buffer_desc;
return_ACPI_STATUS(status); 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 */ /* Get a pointer to the data to be written */
......
...@@ -484,6 +484,8 @@ acpi_status acpi_ex_prep_field_value(struct acpi_create_field_info *info) ...@@ -484,6 +484,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.resource_length = info->resource_length;
} }
obj_desc->field.pin_number_index = info->pin_number_index;
/* Allow full data read from EC address space */ /* Allow full data read from EC address space */
if ((obj_desc->field.region_obj->region.space_id == if ((obj_desc->field.region_obj->region.space_id ==
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment